#include "mbi.h"
#include <libultraship/libultra/gbi.h>
#if 0
#ifndef ULTRA64_GBI_H
#define ULTRA64_GBI_H
#include <stddef.h>

#ifdef _MSC_VER
#ifndef u8
#define u8 uint8_t
#endif

#ifndef u16
#define u16 uint16_t
#endif

#ifndef u32
#define u32 uint32_t
#endif

#ifndef u64
#define u64 uint64_t
#endif

#ifndef s8
#define s8 int8_t
#endif

#ifndef s16
#define s16 int16_t
#endif

#ifndef s32
#define s32 int32_t
#endif

#ifndef s64
#define s64 int64_t
#endif
#endif

#define qs1616(e) ((s32)((e)*0x00010000))

#define IPART(x) ((qs1616(x) >> 16) & 0xFFFF)
#define FPART(x) (qs1616(x) & 0xFFFF)

#define gdSPDefMtx(xx, yx, zx, wx, xy, yy, zy, wy, xz, yz, zz, wz, xw, yw, zw, ww)                                 \
    {                                                                                                              \
        {                                                                                                          \
            (IPART(xx) << 0x10) | IPART(xy), (IPART(xz) << 0x10) | IPART(xw), (IPART(yx) << 0x10) | IPART(yy),     \
                (IPART(yz) << 0x10) | IPART(yw), (IPART(zx) << 0x10) | IPART(zy), (IPART(zz) << 0x10) | IPART(zw), \
                (IPART(wx) << 0x10) | IPART(wy), (IPART(wz) << 0x10) | IPART(ww), (FPART(xx) << 0x10) | FPART(xy), \
                (FPART(xz) << 0x10) | FPART(xw), (FPART(yx) << 0x10) | FPART(yy), (FPART(yz) << 0x10) | FPART(yw), \
                (FPART(zx) << 0x10) | FPART(zy), (FPART(zz) << 0x10) | FPART(zw), (FPART(wx) << 0x10) | FPART(wy), \
                (FPART(wz) << 0x10) | FPART(ww),                                                                   \
        }                                                                                                          \
    }



/* To enable Fast3DEX grucode support, define F3DEX_GBI. */

/* Types */

/* Private macro to wrap other macros in do {...} while (0) */

#define _DW(macro) \
    do {           \
        macro      \
    } while (0)

#define F3DEX_GBI_2

#ifdef F3DEX_GBI_2
#ifndef F3DEX_GBI
#define F3DEX_GBI
#endif
#define G_NOOP 0x00
#define G_RDPHALF_2 0xf1
#define G_SETOTHERMODE_H 0xe3
#define G_SETOTHERMODE_L 0xe2
#define G_RDPHALF_1 0xe1
#define G_SPNOOP 0xe0
#define G_ENDDL 0xdf
#define G_DL 0xde
#define G_LOAD_UCODE 0xdd
#define G_MOVEMEM 0xdc
#define G_MOVEWORD 0xdb
#define G_MTX 0xda
#define G_GEOMETRYMODE 0xd9
#define G_POPMTX 0xd8
#define G_TEXTURE 0xd7
#define G_DMA_IO 0xd6
#define G_SPECIAL_1 0xd5
#define G_SPECIAL_2 0xd4
#define G_SPECIAL_3 0xd3

#define G_VTX 0x01
#define G_MODIFYVTX 0x02
#define G_CULLDL 0x03
#define G_BRANCH_Z 0x04
#define G_TRI1 0x05
#define G_TRI2 0x06
#define G_QUAD 0x07
#define G_LINE3D 0x08
#else              /* F3DEX_GBI_2 */

/* DMA commands: */
#define G_SPNOOP 0 /* handle 0 gracefully */
#define G_MTX 1
#define G_RESERVED0 2 /* not implemeted */
#define G_MOVEMEM 3   /* move a block of memory (up to 4 words) to dmem */
#define G_VTX 4
#define G_RESERVED1 5 /* not implemeted */
#define G_DL 6
#define G_RESERVED2 7     /* not implemeted */
#define G_RESERVED3 8     /* not implemeted */
#define G_SPRITE2D_BASE 9 /* sprite command */

/* IMMEDIATE commands: */
#define G_IMMFIRST -65
#define G_TRI1 (G_IMMFIRST - 0)
#define G_CULLDL (G_IMMFIRST - 1)
#define G_POPMTX (G_IMMFIRST - 2)
#define G_MOVEWORD (G_IMMFIRST - 3)
#define G_TEXTURE (G_IMMFIRST - 4)
#define G_SETOTHERMODE_H (G_IMMFIRST - 5)
#define G_SETOTHERMODE_L (G_IMMFIRST - 6)
#define G_ENDDL (G_IMMFIRST - 7)
#define G_SETGEOMETRYMODE (G_IMMFIRST - 8)
#define G_CLEARGEOMETRYMODE (G_IMMFIRST - 9)
#define G_LINE3D (G_IMMFIRST - 10)
#define G_RDPHALF_1 (G_IMMFIRST - 11)
#define G_RDPHALF_2 (G_IMMFIRST - 12)
#if (defined(F3DEX_GBI) || defined(F3DLP_GBI))
#define G_MODIFYVTX (G_IMMFIRST - 13)
#define G_TRI2 (G_IMMFIRST - 14)
#define G_BRANCH_Z (G_IMMFIRST - 15)
#define G_LOAD_UCODE (G_IMMFIRST - 16)
#else
#define G_RDPHALF_CONT (G_IMMFIRST - 13)
#endif

/* We are overloading 2 of the immediate commands
   to keep the byte alignment of dmem the same */

#define G_SPRITE2D_SCALEFLIP (G_IMMFIRST - 1)
#define G_SPRITE2D_DRAW (G_IMMFIRST - 2)

/* RDP commands: */
#define G_NOOP 0xc0 /*   0 */

#endif                         /* F3DEX_GBI_2 */

/* RDP commands: */
#define G_SETCIMG 0xff         /*  -1 */
#define G_SETZIMG 0xfe         /*  -2 */
#define G_SETTIMG 0xfd         /*  -3 */
#define G_SETCOMBINE 0xfc      /*  -4 */
#define G_SETENVCOLOR 0xfb     /*  -5 */
#define G_SETPRIMCOLOR 0xfa    /*  -6 */
#define G_SETBLENDCOLOR 0xf9   /*  -7 */
#define G_SETFOGCOLOR 0xf8     /*  -8 */
#define G_SETFILLCOLOR 0xf7    /*  -9 */
#define G_FILLRECT 0xf6        /* -10 */
#define G_SETTILE 0xf5         /* -11 */
#define G_LOADTILE 0xf4        /* -12 */
#define G_LOADBLOCK 0xf3       /* -13 */
#define G_SETTILESIZE 0xf2     /* -14 */
#define G_LOADTLUT 0xf0        /* -16 */
#define G_RDPSETOTHERMODE 0xef /* -17 */
#define G_SETPRIMDEPTH 0xee    /* -18 */
#define G_SETSCISSOR 0xed      /* -19 */
#define G_SETCONVERT 0xec      /* -20 */
#define G_SETKEYR 0xeb         /* -21 */
#define G_SETKEYGB 0xea        /* -22 */
#define G_RDPFULLSYNC 0xe9     /* -23 */
#define G_RDPTILESYNC 0xe8     /* -24 */
#define G_RDPPIPESYNC 0xe7     /* -25 */
#define G_RDPLOADSYNC 0xe6     /* -26 */
#define G_TEXRECTFLIP 0xe5     /* -27 */
#define G_TEXRECT 0xe4         /* -28 */

// CUSTOM OTR COMMANDS
#define G_SETTIMG_OTR_HASH 0x20
#define G_SETFB 0x21
#define G_RESETFB 0x22
#define G_SETTIMG_FB 0x23
#define G_VTX_OTR_FILEPATH 0x24
#define G_SETTIMG_OTR_FILEPATH 0x25
#define G_TRI1_OTR 0x26
#define G_DL_OTR_FILEPATH 0x27
#define G_PUSHCD 0x28
#define G_MTX_OTR2 0x29
#define G_DL_OTR_HASH 0x31
#define G_VTX_OTR_HASH 0x32
#define G_MARKER 0x33
#define G_INVALTEXCACHE 0x34
#define G_BRANCH_Z_OTR 0x35
#define G_MTX_OTR 0x36
#define G_TEXRECT_WIDE 0x37
#define G_FILLWIDERECT 0x38

/* GFX Effects */

// RDP Cmd
#define G_SETGRAYSCALE 0x39
#define G_EXTRAGEOMETRYMODE 0x3a
#define G_SETINTENSITY 0x40

/*
 * The following commands are the "generated" RDP commands; the user
 * never sees them, the RSP microcode generates them.
 *
 * The layout of the bits is magical, to save work in the ucode.
 * These id's are -56, -52, -54, -50, -55, -51, -53, -49, ...
 *                                 edge, shade, texture, zbuff bits:  estz
 */
#define G_TRI_FILL 0xc8             /* fill triangle:            11001000 */
#define G_TRI_SHADE 0xcc            /* shade triangle:           11001100 */
#define G_TRI_TXTR 0xca             /* texture triangle:         11001010 */
#define G_TRI_SHADE_TXTR 0xce       /* shade, texture triangle:  11001110 */
#define G_TRI_FILL_ZBUFF 0xc9       /* fill, zbuff triangle:     11001001 */
#define G_TRI_SHADE_ZBUFF 0xcd      /* shade, zbuff triangle:    11001101 */
#define G_TRI_TXTR_ZBUFF 0xcb       /* texture, zbuff triangle:  11001011 */
#define G_TRI_SHADE_TXTR_ZBUFF 0xcf /* shade, txtr, zbuff trngl: 11001111 */

/*
 * A TRI_FILL triangle is just the edges. You need to set the DP
 * to use primcolor, in order to see anything. (it is NOT a triangle
 * that gets rendered in 'fill mode'. Triangles can't be rendered
 * in 'fill mode')
 *
 * A TRI_SHADE is a gouraud triangle that has colors interpolated.
 * Flat-shaded triangles (from the software) are still gouraud shaded,
 * it's just the colors are all the same and the deltas are 0.
 *
 * Other triangle types, and combinations are more obvious.
 */

/* masks to build RDP triangle commands: */
#define G_RDP_TRI_FILL_MASK 0x08
#define G_RDP_TRI_SHADE_MASK 0x04
#define G_RDP_TRI_TXTR_MASK 0x02
#define G_RDP_TRI_ZBUFF_MASK 0x01

/*
 * HACK:
 * This is a dreadful hack. For version 1.0 hardware, there are still
 * some 'bowtie' hangs. This parameter can be increased to avoid
 * the hangs. Every increase of 4 chops one scanline off of every
 * triangle. Values of 4,8,12 should be sufficient to avoid any
 * bowtie hang.
 *
 * Change this value, then recompile ALL of your program (including static
 * display lists!)
 *
 * THIS WILL BE REMOVED FOR HARDWARE VERSION 2.0!
 */
#define BOWTIE_VAL 0

/* gets added to RDP command, in order to test for addres fixup: */
#define G_RDP_ADDR_FIXUP 3 /* |RDP cmds| <= this, do addr fixup */
#ifdef _LANGUAGE_ASSEMBLY
#define G_RDP_TEXRECT_CHECK ((-1 * G_TEXRECTFLIP) & 0xff)
#endif

/* macros for command parsing: */
#define GDMACMD(x) (x)
#define GIMMCMD(x) (G_IMMFIRST - (x))
#define GRDPCMD(x) (0xff - (x))

#define G_DMACMDSIZ 128
#define G_IMMCMDSIZ 64
#define G_RDPCMDSIZ 64

/*
 * Coordinate shift values, number of bits of fraction
 */
#define G_TEXTURE_IMAGE_FRAC 2
#define G_TEXTURE_SCALE_FRAC 16
#define G_SCALE_FRAC 8
#define G_ROTATE_FRAC 16

/*
 * Parameters to graphics commands
 */

/*
 * Data packing macros
 */

/*
 * Maximum z-buffer value, used to initialize the z-buffer.
 * Note : this number is NOT the viewport z-scale constant.
 * See the comment next to G_MAXZ for more info.
 */
#define G_MAXFBZ 0x3fff /* 3b exp, 11b mantissa */

#define GPACK_RGBA5551(r, g, b, a) ((((r) << 8) & 0xf800) | (((g) << 3) & 0x7c0) | (((b) >> 2) & 0x3e) | ((a)&0x1))
#define GPACK_ZDZ(z, dz) ((z) << 2 | (dz))

#define G_MAXFBZ 0x3FFF /* 3b exp, 11b mantissa */

#define GPACK_IA16(i, a) (((i) << 8) | (a))

/*
 * G_MTX: parameter flags
 */
#ifdef F3DEX_GBI_2
#define G_MTX_MODELVIEW 0x00 /* matrix types */
#define G_MTX_PROJECTION 0x04
#define G_MTX_MUL 0x00 /* concat or load */
#define G_MTX_LOAD 0x02
#define G_MTX_NOPUSH 0x00 /* push or not */
#define G_MTX_PUSH 0x01
#else                        /* F3DEX_GBI_2 */
#define G_MTX_MODELVIEW 0x00 /* matrix types */
#define G_MTX_PROJECTION 0x01
#define G_MTX_MUL 0x00 /* concat or load */
#define G_MTX_LOAD 0x02
#define G_MTX_NOPUSH 0x00 /* push or not */
#define G_MTX_PUSH 0x04
#endif /* F3DEX_GBI_2 */

/*
 * flags for G_SETGEOMETRYMODE
 * (this rendering state is maintained in RSP)
 *
 * DO NOT USE THE LOW 8 BITS OF GEOMETRYMODE:
 * The weird bit-ordering is for the micro-code: the lower byte
 * can be OR'd in with G_TRI_SHADE (11001100) to construct
 * the triangle command directly. Don't break it...
 *
 * DO NOT USE THE HIGH 8 BITS OF GEOMETRYMODE:
 * The high byte is OR'd with 0x703 to form the clip code mask.
 * If it is set to 0x04, this will cause near clipping to occur.
 * If it is zero, near clipping will not occur.
 *
 * Further explanation:
 * G_SHADE is necessary in order to see the color that you passed
 * down with the vertex. If G_SHADE isn't set, you need to set the DP
 * appropriately and use primcolor to see anything.
 *
 * G_SHADING_SMOOTH enabled means use all 3 colors of the triangle.
 * If it is not set, then do 'flat shading', where only one vertex color
 * is used (and all 3 vertices are set to that same color by the ucode)
 * See the man page for gSP1Triangle().
 *
 */
#define G_ZBUFFER 0x00000001
#define G_SHADE 0x00000004 /* enable Gouraud interp */
                           /* rest of low byte reserved for setup ucode */
#ifdef F3DEX_GBI_2
#define G_TEXTURE_ENABLE 0x00000000 /* Ignored               */
#define G_SHADING_SMOOTH 0x00200000 /* flat or smooth shaded */
#define G_CULL_FRONT 0x00000200
#define G_CULL_BACK 0x00000400
#define G_CULL_BOTH 0x00000600 /* To make code cleaner */
#else
#define G_TEXTURE_ENABLE 0x00000002 /* Microcode use only */
#define G_SHADING_SMOOTH 0x00000200 /* flat or smooth shaded */
#define G_CULL_FRONT 0x00001000
#define G_CULL_BACK 0x00002000
#define G_CULL_BOTH 0x00003000 /* To make code cleaner */
#endif
#define G_FOG 0x00010000
#define G_LIGHTING 0x00020000
#define G_TEXTURE_GEN 0x00040000
#define G_TEXTURE_GEN_LINEAR 0x00080000
#define G_LOD 0x00100000 /* NOT IMPLEMENTED */
#define G_LIGHTING_POSITIONAL 0x00400000
#if (defined(F3DEX_GBI) || defined(F3DLP_GBI))
#define G_CLIPPING 0x00800000
#else
#define G_CLIPPING 0x00000000
#endif

#ifdef _LANGUAGE_ASSEMBLY
#define G_FOG_H (G_FOG / 0x10000)
#define G_LIGHTING_H (G_LIGHTING / 0x10000)
#define G_TEXTURE_GEN_H (G_TEXTURE_GEN / 0x10000)
#define G_TEXTURE_GEN_LINEAR_H (G_TEXTURE_GEN_LINEAR / 0x10000)
#define G_LOD_H (G_LOD / 0x10000) /* NOT IMPLEMENTED */
#if (defined(F3DEX_GBI) || defined(F3DLP_GBI))
#define G_CLIPPING_H (G_CLIPPING / 0x10000)
#endif
#endif

/*
 * G_EXTRAGEOMETRY flags: set extra custom geometry modes
 */
#define G_EX_INVERT_CULLING 0x00000001
#define G_EX_ALWAYS_EXECUTE_BRANCH 0x00000002

/* Need these defined for Sprite Microcode */
#ifdef _LANGUAGE_ASSEMBLY
#define G_TX_LOADTILE 7
#define G_TX_RENDERTILE 0

#define G_TX_NOMIRROR 0
#define G_TX_WRAP 0
#define G_TX_MIRROR 0x1
#define G_TX_CLAMP 0x2
#define G_TX_NOMASK 0
#define G_TX_NOLOD 0
#endif

/*
 * G_SETIMG fmt: set image formats
 */
#define G_IM_FMT_RGBA 0
#define G_IM_FMT_YUV 1
#define G_IM_FMT_CI 2
#define G_IM_FMT_IA 3
#define G_IM_FMT_I 4

/*
 * G_SETIMG siz: set image pixel size
 */
#define G_IM_SIZ_4b 0
#define G_IM_SIZ_8b 1
#define G_IM_SIZ_16b 2
#define G_IM_SIZ_32b 3
#define G_IM_SIZ_DD 5

#define G_IM_SIZ_4b_BYTES 0
#define G_IM_SIZ_4b_TILE_BYTES G_IM_SIZ_4b_BYTES
#define G_IM_SIZ_4b_LINE_BYTES G_IM_SIZ_4b_BYTES

#define G_IM_SIZ_8b_BYTES 1
#define G_IM_SIZ_8b_TILE_BYTES G_IM_SIZ_8b_BYTES
#define G_IM_SIZ_8b_LINE_BYTES G_IM_SIZ_8b_BYTES

#define G_IM_SIZ_16b_BYTES 2
#define G_IM_SIZ_16b_TILE_BYTES G_IM_SIZ_16b_BYTES
#define G_IM_SIZ_16b_LINE_BYTES G_IM_SIZ_16b_BYTES

#define G_IM_SIZ_32b_BYTES 4
#define G_IM_SIZ_32b_TILE_BYTES 2
#define G_IM_SIZ_32b_LINE_BYTES 2

#define G_IM_SIZ_4b_LOAD_BLOCK G_IM_SIZ_16b
#define G_IM_SIZ_8b_LOAD_BLOCK G_IM_SIZ_16b
#define G_IM_SIZ_16b_LOAD_BLOCK G_IM_SIZ_16b
#define G_IM_SIZ_32b_LOAD_BLOCK G_IM_SIZ_32b

#define G_IM_SIZ_4b_SHIFT 2
#define G_IM_SIZ_8b_SHIFT 1
#define G_IM_SIZ_16b_SHIFT 0
#define G_IM_SIZ_32b_SHIFT 0

#define G_IM_SIZ_4b_INCR 3
#define G_IM_SIZ_8b_INCR 1
#define G_IM_SIZ_16b_INCR 0
#define G_IM_SIZ_32b_INCR 0

/*
 * G_SETCOMBINE: color combine modes
 */
/* Color combiner constants: */
#define G_CCMUX_COMBINED 0
#define G_CCMUX_TEXEL0 1
#define G_CCMUX_TEXEL1 2
#define G_CCMUX_PRIMITIVE 3
#define G_CCMUX_SHADE 4
#define G_CCMUX_ENVIRONMENT 5
#define G_CCMUX_CENTER 6
#define G_CCMUX_SCALE 6
#define G_CCMUX_COMBINED_ALPHA 7
#define G_CCMUX_TEXEL0_ALPHA 8
#define G_CCMUX_TEXEL1_ALPHA 9
#define G_CCMUX_PRIMITIVE_ALPHA 10
#define G_CCMUX_SHADE_ALPHA 11
#define G_CCMUX_ENV_ALPHA 12
#define G_CCMUX_LOD_FRACTION 13
#define G_CCMUX_PRIM_LOD_FRAC 14
#define G_CCMUX_NOISE 7
#define G_CCMUX_K4 7
#define G_CCMUX_K5 15
#define G_CCMUX_1 6
#define G_CCMUX_0 31

/* Alpha combiner constants: */
#define G_ACMUX_COMBINED 0
#define G_ACMUX_TEXEL0 1
#define G_ACMUX_TEXEL1 2
#define G_ACMUX_PRIMITIVE 3
#define G_ACMUX_SHADE 4
#define G_ACMUX_ENVIRONMENT 5
#define G_ACMUX_LOD_FRACTION 0
#define G_ACMUX_PRIM_LOD_FRAC 6
#define G_ACMUX_1 6
#define G_ACMUX_0 7

/* typical CC cycle 1 modes */
#define G_CC_PRIMITIVE 0, 0, 0, PRIMITIVE, 0, 0, 0, PRIMITIVE
#define G_CC_SHADE 0, 0, 0, SHADE, 0, 0, 0, SHADE
#define G_CC_MODULATEI TEXEL0, 0, SHADE, 0, 0, 0, 0, SHADE
#define G_CC_MODULATEIA TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0
#define G_CC_MODULATEIDECALA TEXEL0, 0, SHADE, 0, 0, 0, 0, TEXEL0
#define G_CC_MODULATERGB G_CC_MODULATEI
#define G_CC_MODULATERGBA G_CC_MODULATEIA
#define G_CC_MODULATERGBDECALA G_CC_MODULATEIDECALA
#define G_CC_MODULATEI_PRIM TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE
#define G_CC_MODULATEIA_PRIM TEXEL0, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0
#define G_CC_MODULATEIDECALA_PRIM TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, TEXEL0
#define G_CC_MODULATERGB_PRIM G_CC_MODULATEI_PRIM
#define G_CC_MODULATERGBA_PRIM G_CC_MODULATEIA_PRIM
#define G_CC_MODULATERGBDECALA_PRIM G_CC_MODULATEIDECALA_PRIM
#define G_CC_DECALRGB 0, 0, 0, TEXEL0, 0, 0, 0, SHADE
#define G_CC_DECALRGBA 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0
#define G_CC_BLENDI ENVIRONMENT, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE
#define G_CC_BLENDIA ENVIRONMENT, SHADE, TEXEL0, SHADE, TEXEL0, 0, SHADE, 0
#define G_CC_BLENDIDECALA ENVIRONMENT, SHADE, TEXEL0, SHADE, 0, 0, 0, TEXEL0
#define G_CC_BLENDRGBA TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, 0, 0, 0, SHADE
#define G_CC_BLENDRGBDECALA TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, 0, 0, 0, TEXEL0
#define G_CC_ADDRGB 1, 0, TEXEL0, SHADE, 0, 0, 0, SHADE
#define G_CC_ADDRGBDECALA 1, 0, TEXEL0, SHADE, 0, 0, 0, TEXEL0
#define G_CC_REFLECTRGB ENVIRONMENT, 0, TEXEL0, SHADE, 0, 0, 0, SHADE
#define G_CC_REFLECTRGBDECALA ENVIRONMENT, 0, TEXEL0, SHADE, 0, 0, 0, TEXEL0
#define G_CC_HILITERGB PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE
#define G_CC_HILITERGBA PRIMITIVE, SHADE, TEXEL0, SHADE, PRIMITIVE, SHADE, TEXEL0, SHADE
#define G_CC_HILITERGBDECALA PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, TEXEL0
#define G_CC_SHADEDECALA 0, 0, 0, SHADE, 0, 0, 0, TEXEL0
#define G_CC_BLENDPE PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, SHADE, 0
#define G_CC_BLENDPEDECALA PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, 0, 0, 0, TEXEL0

/* oddball modes */
#define _G_CC_BLENDPE ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, TEXEL0, 0, SHADE, 0
#define _G_CC_BLENDPEDECALA ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, 0, 0, 0, TEXEL0
#define _G_CC_TWOCOLORTEX PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE
/* used for 1-cycle sparse mip-maps, primitive color has color of lowest LOD */
#define _G_CC_SPARSEST PRIMITIVE, TEXEL0, LOD_FRACTION, TEXEL0, PRIMITIVE, TEXEL0, LOD_FRACTION, TEXEL0
#define G_CC_TEMPLERP TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0, TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0

/* typical CC cycle 1 modes, usually followed by other cycle 2 modes */
#define G_CC_TRILERP TEXEL1, TEXEL0, LOD_FRACTION, TEXEL0, TEXEL1, TEXEL0, LOD_FRACTION, TEXEL0
#define G_CC_INTERFERENCE TEXEL0, 0, TEXEL1, 0, TEXEL0, 0, TEXEL1, 0

/*
 *  One-cycle color convert operation
 */
#define G_CC_1CYUV2RGB TEXEL0, K4, K5, TEXEL0, 0, 0, 0, SHADE

/*
 *  NOTE: YUV2RGB expects TF step1 color conversion to occur in 2nd clock.
 * Therefore, CC looks for step1 results in TEXEL1
 */
#define G_CC_YUV2RGB TEXEL1, K4, K5, TEXEL1, 0, 0, 0, 0

/* typical CC cycle 2 modes */
#define G_CC_PASS2 0, 0, 0, COMBINED, 0, 0, 0, COMBINED
#define G_CC_MODULATEI2 COMBINED, 0, SHADE, 0, 0, 0, 0, SHADE
#define G_CC_MODULATEIA2 COMBINED, 0, SHADE, 0, COMBINED, 0, SHADE, 0
#define G_CC_MODULATERGB2 G_CC_MODULATEI2
#define G_CC_MODULATERGBA2 G_CC_MODULATEIA2
#define G_CC_MODULATEI_PRIM2 COMBINED, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE
#define G_CC_MODULATEIA_PRIM2 COMBINED, 0, PRIMITIVE, 0, COMBINED, 0, PRIMITIVE, 0
#define G_CC_MODULATERGB_PRIM2 G_CC_MODULATEI_PRIM2
#define G_CC_MODULATERGBA_PRIM2 G_CC_MODULATEIA_PRIM2
#define G_CC_DECALRGB2 0, 0, 0, COMBINED, 0, 0, 0, SHADE
/*
 * ?
#define G_CC_DECALRGBA2     COMBINED, SHADE, COMBINED_ALPHA, SHADE, 0, 0, 0, SHADE
*/
#define G_CC_BLENDI2 ENVIRONMENT, SHADE, COMBINED, SHADE, 0, 0, 0, SHADE
#define G_CC_BLENDIA2 ENVIRONMENT, SHADE, COMBINED, SHADE, COMBINED, 0, SHADE, 0
#define G_CC_CHROMA_KEY2 TEXEL0, CENTER, SCALE, 0, 0, 0, 0, 0
#define G_CC_HILITERGB2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, SHADE
#define G_CC_HILITERGBA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, ENVIRONMENT, COMBINED, TEXEL0, COMBINED
#define G_CC_HILITERGBDECALA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, TEXEL0
#define G_CC_HILITERGBPASSA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, COMBINED

/*
 * G_SETOTHERMODE_L sft: shift count
 */
#define G_MDSFT_ALPHACOMPARE 0
#define G_MDSFT_ZSRCSEL 2
#define G_MDSFT_RENDERMODE 3
#define G_MDSFT_BLENDER 16

/*
 * G_SETOTHERMODE_H sft: shift count
 */
#define G_MDSFT_BLENDMASK 0 /* unsupported */
#define G_MDSFT_ALPHADITHER 4
#define G_MDSFT_RGBDITHER 6

#define G_MDSFT_COMBKEY 8
#define G_MDSFT_TEXTCONV 9
#define G_MDSFT_TEXTFILT 12
#define G_MDSFT_TEXTLUT 14
#define G_MDSFT_TEXTLOD 16
#define G_MDSFT_TEXTDETAIL 17
#define G_MDSFT_TEXTPERSP 19
#define G_MDSFT_CYCLETYPE 20
#define G_MDSFT_COLORDITHER 22 /* unsupported in HW 2.0 */
#define G_MDSFT_PIPELINE 23

/* G_SETOTHERMODE_H gPipelineMode */
#define G_PM_1PRIMITIVE (1 << G_MDSFT_PIPELINE)
#define G_PM_NPRIMITIVE (0 << G_MDSFT_PIPELINE)

/* G_SETOTHERMODE_H gSetCycleType */
#define G_CYC_1CYCLE (0 << G_MDSFT_CYCLETYPE)
#define G_CYC_2CYCLE (1 << G_MDSFT_CYCLETYPE)
#define G_CYC_COPY (2 << G_MDSFT_CYCLETYPE)
#define G_CYC_FILL (3 << G_MDSFT_CYCLETYPE)

/* G_SETOTHERMODE_H gSetTexturePersp */
#define G_TP_NONE (0 << G_MDSFT_TEXTPERSP)
#define G_TP_PERSP (1 << G_MDSFT_TEXTPERSP)

/* G_SETOTHERMODE_H gSetTextureDetail */
#define G_TD_CLAMP (0 << G_MDSFT_TEXTDETAIL)
#define G_TD_SHARPEN (1 << G_MDSFT_TEXTDETAIL)
#define G_TD_DETAIL (2 << G_MDSFT_TEXTDETAIL)

/* G_SETOTHERMODE_H gSetTextureLOD */
#define G_TL_TILE (0 << G_MDSFT_TEXTLOD)
#define G_TL_LOD (1 << G_MDSFT_TEXTLOD)

/* G_SETOTHERMODE_H gSetTextureLUT */
#define G_TT_NONE (0 << G_MDSFT_TEXTLUT)
#define G_TT_RGBA16 (2 << G_MDSFT_TEXTLUT)
#define G_TT_IA16 (3 << G_MDSFT_TEXTLUT)

/* G_SETOTHERMODE_H gSetTextureFilter */
#define G_TF_POINT (0 << G_MDSFT_TEXTFILT)
#define G_TF_AVERAGE (3 << G_MDSFT_TEXTFILT)
#define G_TF_BILERP (2 << G_MDSFT_TEXTFILT)

/* G_SETOTHERMODE_H gSetTextureConvert */
#define G_TC_CONV (0 << G_MDSFT_TEXTCONV)
#define G_TC_FILTCONV (5 << G_MDSFT_TEXTCONV)
#define G_TC_FILT (6 << G_MDSFT_TEXTCONV)

/* G_SETOTHERMODE_H gSetCombineKey */
#define G_CK_NONE (0 << G_MDSFT_COMBKEY)
#define G_CK_KEY (1 << G_MDSFT_COMBKEY)

/* G_SETOTHERMODE_H gSetColorDither */
#define G_CD_MAGICSQ (0 << G_MDSFT_RGBDITHER)
#define G_CD_BAYER (1 << G_MDSFT_RGBDITHER)
#define G_CD_NOISE (2 << G_MDSFT_RGBDITHER)

#ifndef _HW_VERSION_1
#define G_CD_DISABLE (3 << G_MDSFT_RGBDITHER)
#define G_CD_ENABLE G_CD_NOISE /* HW 1.0 compatibility mode */
#else
#define G_CD_ENABLE (1 << G_MDSFT_COLORDITHER)
#define G_CD_DISABLE (0 << G_MDSFT_COLORDITHER)
#endif

/* G_SETOTHERMODE_H gSetAlphaDither */
#define G_AD_PATTERN (0 << G_MDSFT_ALPHADITHER)
#define G_AD_NOTPATTERN (1 << G_MDSFT_ALPHADITHER)
#define G_AD_NOISE (2 << G_MDSFT_ALPHADITHER)
#define G_AD_DISABLE (3 << G_MDSFT_ALPHADITHER)

/* G_SETOTHERMODE_L gSetAlphaCompare */
#define G_AC_NONE (0 << G_MDSFT_ALPHACOMPARE)
#define G_AC_THRESHOLD (1 << G_MDSFT_ALPHACOMPARE)
#define G_AC_DITHER (3 << G_MDSFT_ALPHACOMPARE)

/* G_SETOTHERMODE_L gSetDepthSource */
#define G_ZS_PIXEL (0 << G_MDSFT_ZSRCSEL)
#define G_ZS_PRIM (1 << G_MDSFT_ZSRCSEL)

/* G_SETOTHERMODE_L gSetRenderMode */
#define AA_EN 0x8
#define Z_CMP 0x10
#define Z_UPD 0x20
#define IM_RD 0x40
#define CLR_ON_CVG 0x80
#define CVG_DST_CLAMP 0
#define CVG_DST_WRAP 0x100
#define CVG_DST_FULL 0x200
#define CVG_DST_SAVE 0x300
#define ZMODE_OPA 0
#define ZMODE_INTER 0x400
#define ZMODE_XLU 0x800
#define ZMODE_DEC 0xc00
#define CVG_X_ALPHA 0x1000
#define ALPHA_CVG_SEL 0x2000
#define FORCE_BL 0x4000
#define TEX_EDGE 0x0000 /* used to be 0x8000 */

#define G_BL_CLR_IN 0
#define G_BL_CLR_MEM 1
#define G_BL_CLR_BL 2
#define G_BL_CLR_FOG 3
#define G_BL_1MA 0
#define G_BL_A_MEM 1
#define G_BL_A_IN 0
#define G_BL_A_FOG 1
#define G_BL_A_SHADE 2
#define G_BL_1 2
#define G_BL_0 3

#define GBL_c1(m1a, m1b, m2a, m2b) (m1a) << 30 | (m1b) << 26 | (m2a) << 22 | (m2b) << 18
#define GBL_c2(m1a, m1b, m2a, m2b) (m1a) << 28 | (m1b) << 24 | (m2a) << 20 | (m2b) << 16

#define RM_AA_ZB_OPA_SURF(clk)                                                  \
    AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_RA_ZB_OPA_SURF(clk)                                          \
    AA_EN | Z_CMP | Z_UPD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_AA_ZB_XLU_SURF(clk)                                                 \
    AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | ZMODE_XLU | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_ZB_OPA_DECAL(clk)                                        \
    AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | ALPHA_CVG_SEL | ZMODE_DEC | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_RA_ZB_OPA_DECAL(clk)                                \
    AA_EN | Z_CMP | CVG_DST_WRAP | ALPHA_CVG_SEL | ZMODE_DEC | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_AA_ZB_XLU_DECAL(clk)                                                \
    AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | ZMODE_DEC | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_ZB_OPA_INTER(clk)                                                   \
    AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ALPHA_CVG_SEL | ZMODE_INTER | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_RA_ZB_OPA_INTER(clk)                                           \
    AA_EN | Z_CMP | Z_UPD | CVG_DST_CLAMP | ALPHA_CVG_SEL | ZMODE_INTER | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_AA_ZB_XLU_INTER(clk)                                                  \
    AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | ZMODE_INTER | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_ZB_XLU_LINE(clk)                                                                   \
    AA_EN | Z_CMP | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_XLU | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_ZB_DEC_LINE(clk)                                                                  \
    AA_EN | Z_CMP | IM_RD | CVG_DST_SAVE | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_DEC | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_ZB_TEX_EDGE(clk)                                                                           \
    AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_AA_ZB_TEX_INTER(clk)                                                                            \
    AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_INTER | TEX_EDGE | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_AA_ZB_SUB_SURF(clk)                                                 \
    AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_AA_ZB_PCL_SURF(clk)                                                \
    AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | G_AC_DITHER | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_ZB_OPA_TERR(clk)                                                  \
    AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_ZB_TEX_TERR(clk)                                                                           \
    AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_ZB_SUB_TERR(clk)                                                 \
    AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_OPA_SURF(clk)                                     \
    AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_RA_OPA_SURF(clk) \
    AA_EN | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_AA_XLU_SURF(clk)                                            \
    AA_EN | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | ZMODE_OPA | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_XLU_LINE(clk)                                                              \
    AA_EN | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_OPA | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_DEC_LINE(clk)                                                             \
    AA_EN | IM_RD | CVG_DST_FULL | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_OPA | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_TEX_EDGE(clk)                                                              \
    AA_EN | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_AA_SUB_SURF(clk)                                    \
    AA_EN | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_AA_PCL_SURF(clk) \
    AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | G_AC_DITHER | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_OPA_TERR(clk)                                     \
    AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_TEX_TERR(clk)                                                              \
    AA_EN | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_AA_SUB_TERR(clk)                                    \
    AA_EN | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_ZB_OPA_SURF(clk)                                    \
    Z_CMP | Z_UPD | CVG_DST_FULL | ALPHA_CVG_SEL | ZMODE_OPA | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_ZB_XLU_SURF(clk) \
    Z_CMP | IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_XLU | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_ZB_OPA_DECAL(clk) \
    Z_CMP | CVG_DST_FULL | ALPHA_CVG_SEL | ZMODE_DEC | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define RM_ZB_XLU_DECAL(clk) \
    Z_CMP | IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_DEC | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_ZB_CLD_SURF(clk) \
    Z_CMP | IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_XLU | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_ZB_OVL_SURF(clk) \
    Z_CMP | IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_DEC | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_ZB_PCL_SURF(clk) \
    Z_CMP | Z_UPD | CVG_DST_FULL | ZMODE_OPA | G_AC_DITHER | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)

#define RM_OPA_SURF(clk) CVG_DST_CLAMP | FORCE_BL | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)

#define RM_XLU_SURF(clk) \
    IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_TEX_EDGE(clk)                                                                    \
    CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_OPA | TEX_EDGE | AA_EN | \
        GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)

#define RM_CLD_SURF(clk) \
    IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)

#define RM_PCL_SURF(clk) \
    CVG_DST_FULL | FORCE_BL | ZMODE_OPA | G_AC_DITHER | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)

#define RM_ADD(clk) \
    IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_1)

#define RM_NOOP(clk) GBL_c##clk(0, 0, 0, 0)

#define RM_VISCVG(clk) IM_RD | FORCE_BL | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_BL, G_BL_A_MEM)

/* for rendering to an 8-bit framebuffer */
#define RM_OPA_CI(clk) CVG_DST_CLAMP | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)

#define G_RM_AA_ZB_OPA_SURF RM_AA_ZB_OPA_SURF(1)
#define G_RM_AA_ZB_OPA_SURF2 RM_AA_ZB_OPA_SURF(2)
#define G_RM_AA_ZB_XLU_SURF RM_AA_ZB_XLU_SURF(1)
#define G_RM_AA_ZB_XLU_SURF2 RM_AA_ZB_XLU_SURF(2)
#define G_RM_AA_ZB_OPA_DECAL RM_AA_ZB_OPA_DECAL(1)
#define G_RM_AA_ZB_OPA_DECAL2 RM_AA_ZB_OPA_DECAL(2)
#define G_RM_AA_ZB_XLU_DECAL RM_AA_ZB_XLU_DECAL(1)
#define G_RM_AA_ZB_XLU_DECAL2 RM_AA_ZB_XLU_DECAL(2)
#define G_RM_AA_ZB_OPA_INTER RM_AA_ZB_OPA_INTER(1)
#define G_RM_AA_ZB_OPA_INTER2 RM_AA_ZB_OPA_INTER(2)
#define G_RM_AA_ZB_XLU_INTER RM_AA_ZB_XLU_INTER(1)
#define G_RM_AA_ZB_XLU_INTER2 RM_AA_ZB_XLU_INTER(2)
#define G_RM_AA_ZB_XLU_LINE RM_AA_ZB_XLU_LINE(1)
#define G_RM_AA_ZB_XLU_LINE2 RM_AA_ZB_XLU_LINE(2)
#define G_RM_AA_ZB_DEC_LINE RM_AA_ZB_DEC_LINE(1)
#define G_RM_AA_ZB_DEC_LINE2 RM_AA_ZB_DEC_LINE(2)
#define G_RM_AA_ZB_TEX_EDGE RM_AA_ZB_TEX_EDGE(1)
#define G_RM_AA_ZB_TEX_EDGE2 RM_AA_ZB_TEX_EDGE(2)
#define G_RM_AA_ZB_TEX_INTER RM_AA_ZB_TEX_INTER(1)
#define G_RM_AA_ZB_TEX_INTER2 RM_AA_ZB_TEX_INTER(2)
#define G_RM_AA_ZB_SUB_SURF RM_AA_ZB_SUB_SURF(1)
#define G_RM_AA_ZB_SUB_SURF2 RM_AA_ZB_SUB_SURF(2)
#define G_RM_AA_ZB_PCL_SURF RM_AA_ZB_PCL_SURF(1)
#define G_RM_AA_ZB_PCL_SURF2 RM_AA_ZB_PCL_SURF(2)
#define G_RM_AA_ZB_OPA_TERR RM_AA_ZB_OPA_TERR(1)
#define G_RM_AA_ZB_OPA_TERR2 RM_AA_ZB_OPA_TERR(2)
#define G_RM_AA_ZB_TEX_TERR RM_AA_ZB_TEX_TERR(1)
#define G_RM_AA_ZB_TEX_TERR2 RM_AA_ZB_TEX_TERR(2)
#define G_RM_AA_ZB_SUB_TERR RM_AA_ZB_SUB_TERR(1)
#define G_RM_AA_ZB_SUB_TERR2 RM_AA_ZB_SUB_TERR(2)

#define G_RM_RA_ZB_OPA_SURF RM_RA_ZB_OPA_SURF(1)
#define G_RM_RA_ZB_OPA_SURF2 RM_RA_ZB_OPA_SURF(2)
#define G_RM_RA_ZB_OPA_DECAL RM_RA_ZB_OPA_DECAL(1)
#define G_RM_RA_ZB_OPA_DECAL2 RM_RA_ZB_OPA_DECAL(2)
#define G_RM_RA_ZB_OPA_INTER RM_RA_ZB_OPA_INTER(1)
#define G_RM_RA_ZB_OPA_INTER2 RM_RA_ZB_OPA_INTER(2)

#define G_RM_AA_OPA_SURF RM_AA_OPA_SURF(1)
#define G_RM_AA_OPA_SURF2 RM_AA_OPA_SURF(2)
#define G_RM_AA_XLU_SURF RM_AA_XLU_SURF(1)
#define G_RM_AA_XLU_SURF2 RM_AA_XLU_SURF(2)
#define G_RM_AA_XLU_LINE RM_AA_XLU_LINE(1)
#define G_RM_AA_XLU_LINE2 RM_AA_XLU_LINE(2)
#define G_RM_AA_DEC_LINE RM_AA_DEC_LINE(1)
#define G_RM_AA_DEC_LINE2 RM_AA_DEC_LINE(2)
#define G_RM_AA_TEX_EDGE RM_AA_TEX_EDGE(1)
#define G_RM_AA_TEX_EDGE2 RM_AA_TEX_EDGE(2)
#define G_RM_AA_SUB_SURF RM_AA_SUB_SURF(1)
#define G_RM_AA_SUB_SURF2 RM_AA_SUB_SURF(2)
#define G_RM_AA_PCL_SURF RM_AA_PCL_SURF(1)
#define G_RM_AA_PCL_SURF2 RM_AA_PCL_SURF(2)
#define G_RM_AA_OPA_TERR RM_AA_OPA_TERR(1)
#define G_RM_AA_OPA_TERR2 RM_AA_OPA_TERR(2)
#define G_RM_AA_TEX_TERR RM_AA_TEX_TERR(1)
#define G_RM_AA_TEX_TERR2 RM_AA_TEX_TERR(2)
#define G_RM_AA_SUB_TERR RM_AA_SUB_TERR(1)
#define G_RM_AA_SUB_TERR2 RM_AA_SUB_TERR(2)

#define G_RM_RA_OPA_SURF RM_RA_OPA_SURF(1)
#define G_RM_RA_OPA_SURF2 RM_RA_OPA_SURF(2)

#define G_RM_ZB_OPA_SURF RM_ZB_OPA_SURF(1)
#define G_RM_ZB_OPA_SURF2 RM_ZB_OPA_SURF(2)
#define G_RM_ZB_XLU_SURF RM_ZB_XLU_SURF(1)
#define G_RM_ZB_XLU_SURF2 RM_ZB_XLU_SURF(2)
#define G_RM_ZB_OPA_DECAL RM_ZB_OPA_DECAL(1)
#define G_RM_ZB_OPA_DECAL2 RM_ZB_OPA_DECAL(2)
#define G_RM_ZB_XLU_DECAL RM_ZB_XLU_DECAL(1)
#define G_RM_ZB_XLU_DECAL2 RM_ZB_XLU_DECAL(2)
#define G_RM_ZB_CLD_SURF RM_ZB_CLD_SURF(1)
#define G_RM_ZB_CLD_SURF2 RM_ZB_CLD_SURF(2)
#define G_RM_ZB_OVL_SURF RM_ZB_OVL_SURF(1)
#define G_RM_ZB_OVL_SURF2 RM_ZB_OVL_SURF(2)
#define G_RM_ZB_PCL_SURF RM_ZB_PCL_SURF(1)
#define G_RM_ZB_PCL_SURF2 RM_ZB_PCL_SURF(2)

#define G_RM_OPA_SURF RM_OPA_SURF(1)
#define G_RM_OPA_SURF2 RM_OPA_SURF(2)
#define G_RM_XLU_SURF RM_XLU_SURF(1)
#define G_RM_XLU_SURF2 RM_XLU_SURF(2)
#define G_RM_CLD_SURF RM_CLD_SURF(1)
#define G_RM_CLD_SURF2 RM_CLD_SURF(2)
#define G_RM_TEX_EDGE RM_TEX_EDGE(1)
#define G_RM_TEX_EDGE2 RM_TEX_EDGE(2)
#define G_RM_PCL_SURF RM_PCL_SURF(1)
#define G_RM_PCL_SURF2 RM_PCL_SURF(2)
#define G_RM_ADD RM_ADD(1)
#define G_RM_ADD2 RM_ADD(2)
#define G_RM_NOOP RM_NOOP(1)
#define G_RM_NOOP2 RM_NOOP(2)
#define G_RM_VISCVG RM_VISCVG(1)
#define G_RM_VISCVG2 RM_VISCVG(2)
#define G_RM_OPA_CI RM_OPA_CI(1)
#define G_RM_OPA_CI2 RM_OPA_CI(2)

#define G_RM_FOG_SHADE_A GBL_c1(G_BL_CLR_FOG, G_BL_A_SHADE, G_BL_CLR_IN, G_BL_1MA)
#define G_RM_FOG_PRIM_A GBL_c1(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_IN, G_BL_1MA)
#define G_RM_PASS GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)

/*
 * G_SETCONVERT: K0-5
 */
#define G_CV_K0 175
#define G_CV_K1 -43
#define G_CV_K2 -89
#define G_CV_K3 222
#define G_CV_K4 114
#define G_CV_K5 42

/*
 * G_SETSCISSOR: interlace mode
 */
#define G_SC_NON_INTERLACE 0
#define G_SC_ODD_INTERLACE 3
#define G_SC_EVEN_INTERLACE 2

/* flags to inhibit pushing of the display list (on branch) */
#define G_DL_PUSH 0x00
#define G_DL_NOPUSH 0x01

#if defined(_MSC_VER) || defined(__GNUC__)
#define _LANGUAGE_C
#endif

/*
 * BEGIN C-specific section: (typedef's)
 */
#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS)

/*
 * Data Structures
 *
 * NOTE:
 * The DMA transfer hardware requires 64-bit aligned, 64-bit multiple-
 * sized transfers. This important hardware optimization is unfortunately
 * reflected in the programming interface, with some structures
 * padded and alignment enforced.
 *
 * Since structures are aligned to the boundary of the "worst-case"
 * element, we can't depend on the C compiler to align things
 * properly.
 *
 * 64-bit structure alignment is enforced by wrapping structures with
 * unions that contain a dummy "long long int".  Why this works is
 * explained in the ANSI C Spec, or on page 186 of the second edition
 * of K&R, "The C Programming Language".
 *
 * The price we pay for this is a little awkwardness referencing the
 * structures through the union. There is no memory penalty, since
 * all the structures are at least 64-bits the dummy alignment field
 * does not increase the size of the union.
 *
 * Static initialization of these union structures works because
 * the ANSI C spec states that static initialization for unions
 * works by using the first union element. We put the dummy alignment
 * field last for this reason.
 *
 * (it's possible a newer 64-bit compiler from MIPS might make this
 * easier with a flag, but we can't wait for it...)
 *
 */

/*
 * Vertex (set up for use with colors)
 */
typedef struct {
    short ob[3]; /* x, y, z */
    unsigned short flag;
    short tc[2];         /* texture coord */
    unsigned char cn[4]; /* color & alpha */
} Vtx_t;

/*
 * Vertex (set up for use with normals)
 */
typedef struct {
    short ob[3]; /* x, y, z */
    unsigned short flag;
    short tc[2];      /* texture coord */
    signed char n[3]; /* normal */
    unsigned char a;  /* alpha  */
} Vtx_tn;

typedef union {
    Vtx_t v;  /* Use this one for colors  */
    Vtx_tn n; /* Use this one for normals */
    long long int force_structure_alignment;
} Vtx;

/*
 * Sprite structure
 */

typedef struct {
    void* SourceImagePointer;
    void* TlutPointer;
    short Stride;
    short SubImageWidth;
    short SubImageHeight;
    char SourceImageType;
    char SourceImageBitSize;
    short SourceImageOffsetS;
    short SourceImageOffsetT;
    /* 20 bytes for above */

    /* padding to bring structure size to 64 bit allignment */
    char dummy[4];

} uSprite_t;

typedef union {
    uSprite_t s;

    /* Need to make sure this is 64 bit aligned */
    long long int force_structure_allignment[3];
} uSprite;

/*
 * Triangle face
 */
typedef struct {
    unsigned char flag;
    unsigned char v[3];
} Tri;

/*
 * Viewport
 */

/*
 *
 * This magic value is the maximum INTEGER z-range of the hardware
 * (there are also 16-bits of fraction, which are introduced during
 * any transformations). This is not just a good idea, it's the law.
 * Feeding the hardware eventual z-coordinates (after any transforms
 * or scaling) bigger than this, will not work.
 *
 * This number is DIFFERENT than G_MAXFBZ, which is the maximum value
 * you want to use to initialize the z-buffer.
 *
 * The reason these are different is mildly interesting, but too long
 * to explain here. It is basically the result of optimizations in the
 * hardware. A more generic API might hide this detail from the users,
 * but we don't have the ucode to do that...
 *
 */
#define G_MAXZ 0x03ff /* 10 bits of integer screen-Z precision */

/*
 * The viewport structure elements have 2 bits of fraction, necessary
 * to accomodate the sub-pixel positioning scaling for the hardware.
 * This can also be exploited to handle odd-sized viewports.
 *
 * Accounting for these fractional bits, using the default projection
 * and viewing matrices, the viewport structure is initialized thusly:
 *
 *      (SCREEN_WD/2)*4, (SCREEN_HT/2)*4, G_MAXZ, 0,
 *      (SCREEN_WD/2)*4, (SCREEN_HT/2)*4, 0, 0,
 */
typedef struct {
    short vscale[4]; /* scale, 2 bits fraction */
    short vtrans[4]; /* translate, 2 bits fraction */
    /* both the above arrays are padded to 64-bit boundary */
} Vp_t;

typedef union {
    Vp_t vp;
    long long int force_structure_alignment;
} Vp;

/*
 * MOVEMEM indices
 *
 * Each of these indexes an entry in a dmem table
 * which points to a 1-4 word block of dmem in
 * which to store a 1-4 word DMA.
 *
 */
#ifdef F3DEX_GBI_2
/* 0,4 are reserved by G_MTX */
#define G_MV_MMTX 2
#define G_MV_PMTX 6
#define G_MV_VIEWPORT 8
#define G_MV_LIGHT 10
#define G_MV_POINT 12
#define G_MV_MATRIX 14 /* NOTE: this is in moveword table */
#define G_MVO_LOOKATX (0 * 24)
#define G_MVO_LOOKATY (1 * 24)
#define G_MVO_L0 (2 * 24)
#define G_MVO_L1 (3 * 24)
#define G_MVO_L2 (4 * 24)
#define G_MVO_L3 (5 * 24)
#define G_MVO_L4 (6 * 24)
#define G_MVO_L5 (7 * 24)
#define G_MVO_L6 (8 * 24)
#define G_MVO_L7 (9 * 24)
#else /* F3DEX_GBI_2 */
#define G_MV_VIEWPORT 0x80
#define G_MV_LOOKATY 0x82
#define G_MV_LOOKATX 0x84
#define G_MV_L0 0x86
#define G_MV_L1 0x88
#define G_MV_L2 0x8a
#define G_MV_L3 0x8c
#define G_MV_L4 0x8e
#define G_MV_L5 0x90
#define G_MV_L6 0x92
#define G_MV_L7 0x94
#define G_MV_TXTATT 0x96
#define G_MV_MATRIX_1 0x9e /* NOTE: this is in moveword table */
#define G_MV_MATRIX_2 0x98
#define G_MV_MATRIX_3 0x9a
#define G_MV_MATRIX_4 0x9c
#endif                   /* F3DEX_GBI_2 */

/*
 * MOVEWORD indices
 *
 * Each of these indexes an entry in a dmem table
 * which points to a word in dmem in dmem where
 * an immediate word will be stored.
 *
 */
#define G_MW_MATRIX 0x00 /* NOTE: also used by movemem */
#define G_MW_NUMLIGHT 0x02
#define G_MW_CLIP 0x04
#define G_MW_SEGMENT 0x06
#define G_MW_FOG 0x08
#define G_MW_LIGHTCOL 0x0a
#ifdef F3DEX_GBI_2
#define G_MW_FORCEMTX 0x0c
#else /* F3DEX_GBI_2 */
#define G_MW_POINTS 0x0c
#endif /* F3DEX_GBI_2 */
#define G_MW_PERSPNORM 0x0e

/*
 * These are offsets from the address in the dmem table
 */
#define G_MWO_NUMLIGHT 0x00
#define G_MWO_CLIP_RNX 0x04
#define G_MWO_CLIP_RNY 0x0c
#define G_MWO_CLIP_RPX 0x14
#define G_MWO_CLIP_RPY 0x1c
#define G_MWO_SEGMENT_0 0x00
#define G_MWO_SEGMENT_1 0x01
#define G_MWO_SEGMENT_2 0x02
#define G_MWO_SEGMENT_3 0x03
#define G_MWO_SEGMENT_4 0x04
#define G_MWO_SEGMENT_5 0x05
#define G_MWO_SEGMENT_6 0x06
#define G_MWO_SEGMENT_7 0x07
#define G_MWO_SEGMENT_8 0x08
#define G_MWO_SEGMENT_9 0x09
#define G_MWO_SEGMENT_A 0x0a
#define G_MWO_SEGMENT_B 0x0b
#define G_MWO_SEGMENT_C 0x0c
#define G_MWO_SEGMENT_D 0x0d
#define G_MWO_SEGMENT_E 0x0e
#define G_MWO_SEGMENT_F 0x0f
#define G_MWO_FOG 0x00
#define G_MWO_aLIGHT_1 0x00
#define G_MWO_bLIGHT_1 0x04
#ifdef F3DEX_GBI_2
#define G_MWO_aLIGHT_2 0x18
#define G_MWO_bLIGHT_2 0x1c
#define G_MWO_aLIGHT_3 0x30
#define G_MWO_bLIGHT_3 0x34
#define G_MWO_aLIGHT_4 0x48
#define G_MWO_bLIGHT_4 0x4c
#define G_MWO_aLIGHT_5 0x60
#define G_MWO_bLIGHT_5 0x64
#define G_MWO_aLIGHT_6 0x78
#define G_MWO_bLIGHT_6 0x7c
#define G_MWO_aLIGHT_7 0x90
#define G_MWO_bLIGHT_7 0x94
#define G_MWO_aLIGHT_8 0xa8
#define G_MWO_bLIGHT_8 0xac
#else
#define G_MWO_aLIGHT_2 0x20
#define G_MWO_bLIGHT_2 0x24
#define G_MWO_aLIGHT_3 0x40
#define G_MWO_bLIGHT_3 0x44
#define G_MWO_aLIGHT_4 0x60
#define G_MWO_bLIGHT_4 0x64
#define G_MWO_aLIGHT_5 0x80
#define G_MWO_bLIGHT_5 0x84
#define G_MWO_aLIGHT_6 0xa0
#define G_MWO_bLIGHT_6 0xa4
#define G_MWO_aLIGHT_7 0xc0
#define G_MWO_bLIGHT_7 0xc4
#define G_MWO_aLIGHT_8 0xe0
#define G_MWO_bLIGHT_8 0xe4
#endif
#define G_MWO_MATRIX_XX_XY_I 0x00
#define G_MWO_MATRIX_XZ_XW_I 0x04
#define G_MWO_MATRIX_YX_YY_I 0x08
#define G_MWO_MATRIX_YZ_YW_I 0x0c
#define G_MWO_MATRIX_ZX_ZY_I 0x10
#define G_MWO_MATRIX_ZZ_ZW_I 0x14
#define G_MWO_MATRIX_WX_WY_I 0x18
#define G_MWO_MATRIX_WZ_WW_I 0x1c
#define G_MWO_MATRIX_XX_XY_F 0x20
#define G_MWO_MATRIX_XZ_XW_F 0x24
#define G_MWO_MATRIX_YX_YY_F 0x28
#define G_MWO_MATRIX_YZ_YW_F 0x2c
#define G_MWO_MATRIX_ZX_ZY_F 0x30
#define G_MWO_MATRIX_ZZ_ZW_F 0x34
#define G_MWO_MATRIX_WX_WY_F 0x38
#define G_MWO_MATRIX_WZ_WW_F 0x3c
#define G_MWO_POINT_RGBA 0x10
#define G_MWO_POINT_ST 0x14
#define G_MWO_POINT_XYSCREEN 0x18
#define G_MWO_POINT_ZSCREEN 0x1c

/*
 * Light structure.
 *
 * Note: only directional (infinite) lights are currently supported.
 *
 * Note: the weird order is for the DMEM alignment benefit of
 * the microcode.
 *
 */

typedef struct {
    unsigned char col[3]; /* diffuse light value (rgba) */
    char pad1;
    unsigned char colc[3]; /* copy of diffuse light value (rgba) */
    char pad2;
    signed char dir[3]; /* direction of light (normalized) */
    char pad3;
} Light_t;

// Added in MM
typedef struct {
    unsigned char col[3];
    unsigned char unk3;
    unsigned char colc[3];
    unsigned char unk7;
    short pos[3];
    unsigned char unkE;
} PointLight_t;

typedef struct {
    unsigned char col[3]; /* ambient light value (rgba) */
    char pad1;
    unsigned char colc[3]; /* copy of ambient light value (rgba) */
    char pad2;
} Ambient_t;

typedef struct {
    int x1, y1, x2, y2; /* texture offsets for highlight 1/2 */
} Hilite_t;

typedef union {
    Light_t l;
    PointLight_t p;
    long long int force_structure_alignment[2];
} Light;

typedef union {
    Ambient_t l;
    long long int force_structure_alignment[1];
} Ambient;

typedef struct {
    Ambient a;
    Light l[7];
} Lightsn;

typedef struct {
    Ambient a;
    Light l[1];
} Lights0;

typedef struct {
    Ambient a;
    Light l[1];
} Lights1;

typedef struct {
    Ambient a;
    Light l[2];
} Lights2;

typedef struct {
    Ambient a;
    Light l[3];
} Lights3;

typedef struct {
    Ambient a;
    Light l[4];
} Lights4;

typedef struct {
    Ambient a;
    Light l[5];
} Lights5;

typedef struct {
    Ambient a;
    Light l[6];
} Lights6;

typedef struct {
    Ambient a;
    Light l[7];
} Lights7;

typedef struct {
    Light l[2];
} LookAt;

typedef union {
    Hilite_t h;
    long int force_structure_alignment[4];
} Hilite;

#define gdSPDefLights0(ar, ag, ab)                                 \
    {                                                              \
        { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, {            \
            {                                                      \
                { { 0, 0, 0 }, 0, { 0, 0, 0 }, 0, { 0, 0, 0 }, 0 } \
            }                                                      \
        }                                                          \
    }
#define gdSPDefLights1(ar, ag, ab, r1, g1, b1, x1, y1, z1)                  \
    {                                                                       \
        { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, {                     \
            {                                                               \
                { { r1, g1, b1 }, 0, { r1, g1, b1 }, 0, { x1, y1, z1 }, 0 } \
            }                                                               \
        }                                                                   \
    }
#define gdSPDefLights2(ar, ag, ab, r1, g1, b1, x1, y1, z1, r2, g2, b2, x2, y2, z2) \
    {                                                                              \
        { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, {                            \
            { { { r1, g1, b1 }, 0, { r1, g1, b1 }, 0, { x1, y1, z1 }, 0 } }, {     \
                { { r2, g2, b2 }, 0, { r2, g2, b2 }, 0, { x2, y2, z2 }, 0 }        \
            }                                                                      \
        }                                                                          \
    }
#define gdSPDefLights3(ar, ag, ab, r1, g1, b1, x1, y1, z1, r2, g2, b2, x2, y2, z2, r3, g3, b3, x3, y3, z3) \
    {                                                                                                      \
        { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, {                                                    \
            { { { r1, g1, b1 }, 0, { r1, g1, b1 }, 0, { x1, y1, z1 }, 0 } },                               \
                { { { r2, g2, b2 }, 0, { r2, g2, b2 }, 0, { x2, y2, z2 }, 0 } }, {                         \
                { { r3, g3, b3 }, 0, { r3, g3, b3 }, 0, { x3, y3, z3 }, 0 }                                \
            }                                                                                              \
        }                                                                                                  \
    }
#define gdSPDefLights4(ar, ag, ab, r1, g1, b1, x1, y1, z1, r2, g2, b2, x2, y2, z2, r3, g3, b3, x3, y3, z3, r4, g4, b4, \
                       x4, y4, z4)                                                                                     \
    {                                                                                                                  \
        { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, {                                                                \
            { { { r1, g1, b1 }, 0, { r1, g1, b1 }, 0, { x1, y1, z1 }, 0 } },                                           \
                { { { r2, g2, b2 }, 0, { r2, g2, b2 }, 0, { x2, y2, z2 }, 0 } },                                       \
                { { { r3, g3, b3 }, 0, { r3, g3, b3 }, 0, { x3, y3, z3 }, 0 } }, {                                     \
                { { r4, g4, b4 }, 0, { r4, g4, b4 }, 0, { x4, y4, z4 }, 0 }                                            \
            }                                                                                                          \
        }                                                                                                              \
    }
#define gdSPDefLights5(ar, ag, ab, r1, g1, b1, x1, y1, z1, r2, g2, b2, x2, y2, z2, r3, g3, b3, x3, y3, z3, r4, g4, b4, \
                       x4, y4, z4, r5, g5, b5, x5, y5, z5)                                                             \
    {                                                                                                                  \
        { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, {                                                                \
            { { { r1, g1, b1 }, 0, { r1, g1, b1 }, 0, { x1, y1, z1 }, 0 } },                                           \
                { { { r2, g2, b2 }, 0, { r2, g2, b2 }, 0, { x2, y2, z2 }, 0 } },                                       \
                { { { r3, g3, b3 }, 0, { r3, g3, b3 }, 0, { x3, y3, z3 }, 0 } },                                       \
                { { { r4, g4, b4 }, 0, { r4, g4, b4 }, 0, { x4, y4, z4 }, 0 } }, {                                     \
                { { r5, g5, b5 }, 0, { r5, g5, b5 }, 0, { x5, y5, z5 }, 0 }                                            \
            }                                                                                                          \
        }                                                                                                              \
    }

#define gdSPDefLights6(ar, ag, ab, r1, g1, b1, x1, y1, z1, r2, g2, b2, x2, y2, z2, r3, g3, b3, x3, y3, z3, r4, g4, b4, \
                       x4, y4, z4, r5, g5, b5, x5, y5, z5, r6, g6, b6, x6, y6, z6)                                     \
    {                                                                                                                  \
        { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, {                                                                \
            { { { r1, g1, b1 }, 0, { r1, g1, b1 }, 0, { x1, y1, z1 }, 0 } },                                           \
                { { { r2, g2, b2 }, 0, { r2, g2, b2 }, 0, { x2, y2, z2 }, 0 } },                                       \
                { { { r3, g3, b3 }, 0, { r3, g3, b3 }, 0, { x3, y3, z3 }, 0 } },                                       \
                { { { r4, g4, b4 }, 0, { r4, g4, b4 }, 0, { x4, y4, z4 }, 0 } },                                       \
                { { { r5, g5, b5 }, 0, { r5, g5, b5 }, 0, { x5, y5, z5 }, 0 } }, {                                     \
                { { r6, g6, b6 }, 0, { r6, g6, b6 }, 0, { x6, y6, z6 }, 0 }                                            \
            }                                                                                                          \
        }                                                                                                              \
    }

#define gdSPDefLights7(ar, ag, ab, r1, g1, b1, x1, y1, z1, r2, g2, b2, x2, y2, z2, r3, g3, b3, x3, y3, z3, r4, g4, b4, \
                       x4, y4, z4, r5, g5, b5, x5, y5, z5, r6, g6, b6, x6, y6, z6, r7, g7, b7, x7, y7, z7)             \
    {                                                                                                                  \
        { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, {                                                                \
            { { { r1, g1, b1 }, 0, { r1, g1, b1 }, 0, { x1, y1, z1 }, 0 } },                                           \
                { { { r2, g2, b2 }, 0, { r2, g2, b2 }, 0, { x2, y2, z2 }, 0 } },                                       \
                { { { r3, g3, b3 }, 0, { r3, g3, b3 }, 0, { x3, y3, z3 }, 0 } },                                       \
                { { { r4, g4, b4 }, 0, { r4, g4, b4 }, 0, { x4, y4, z4 }, 0 } },                                       \
                { { { r5, g5, b5 }, 0, { r5, g5, b5 }, 0, { x5, y5, z5 }, 0 } },                                       \
                { { { r6, g6, b6 }, 0, { r6, g6, b6 }, 0, { x6, y6, z6 }, 0 } }, {                                     \
                { { r7, g7, b7 }, 0, { r7, g7, b7 }, 0, { x7, y7, z7 }, 0 }                                            \
            }                                                                                                          \
        }                                                                                                              \
    }

#define gdSPDefLookAt(rightx, righty, rightz, upx, upy, upz)                         \
    {                                                                                \
        {                                                                            \
            { { { 0, 0, 0 }, 0, { 0, 0, 0 }, 0, { rightx, righty, rightz }, 0 } }, { \
                { { 0, 0x80, 0 }, 0, { 0, 0x80, 0 }, 0, { upx, upy, upz }, 0 }       \
            }                                                                        \
        }                                                                            \
    }

/*
 *  Graphics DMA Packet
 */
typedef struct {
    int cmd : 8;
    unsigned int par : 8;
    unsigned int len : 16;
    unsigned int addr;
} Gdma;

/*
 * Graphics Immediate Mode Packet types
 */
typedef struct {
    int cmd : 8;
    int pad : 24;
    Tri tri;
} Gtri;

typedef struct {
    int cmd : 8;
    int pad1 : 24;
    int pad2 : 24;
    unsigned char param : 8;
} Gpopmtx;

/*
 * typedef struct {
 *      int     cmd:8;
 *      int     pad0:24;
 *      int     pad1:4;
 *      int     number:4;
 *      int     base:24;
 * } Gsegment;
 */
typedef struct {
    int cmd : 8;
    int pad0 : 8;
    int mw_index : 8;
    int number : 8;
    int pad1 : 8;
    int base : 24;
} Gsegment;

typedef struct {
    int cmd : 8;
    int pad0 : 8;
    int sft : 8;
    int len : 8;
    unsigned int data : 32;
} GsetothermodeL;

typedef struct {
    int cmd : 8;
    int pad0 : 8;
    int sft : 8;
    int len : 8;
    unsigned int data : 32;
} GsetothermodeH;

typedef struct {
    unsigned char cmd;
    unsigned char lodscale;
    unsigned char tile;
    unsigned char on;
    unsigned short s;
    unsigned short t;
} Gtexture;

typedef struct {
    int cmd : 8;
    int pad : 24;
    Tri line;
} Gline3D;

typedef struct {
    int cmd : 8;
    int pad1 : 24;
    short int pad2;
    short int scale;
} Gperspnorm;

/*
 * RDP Packet types
 */
typedef struct {
    int cmd : 8;
    unsigned int fmt : 3;
    unsigned int siz : 2;
    unsigned int pad : 7;
    unsigned int wd : 12; /* really only 10 bits, extra   */
    unsigned int dram;    /* to account for 1024      */
} Gsetimg;

typedef struct {
    int cmd : 8;
    unsigned int muxs0 : 24;
    unsigned int muxs1 : 32;
} Gsetcombine;

typedef struct {
    int cmd : 8;
    unsigned char pad;
    unsigned char prim_min_level;
    unsigned char prim_level;
    unsigned long color;
} Gsetcolor;

typedef struct {
    int cmd : 8;
    int x0 : 10;
    int x0frac : 2;
    int y0 : 10;
    int y0frac : 2;
    unsigned int pad : 8;
    int x1 : 10;
    int x1frac : 2;
    int y1 : 10;
    int y1frac : 2;
} Gfillrect;

typedef struct {
    int cmd : 8;
    unsigned int fmt : 3;
    unsigned int siz : 2;
    unsigned int pad0 : 1;
    unsigned int line : 9;
    unsigned int tmem : 9;
    unsigned int pad1 : 5;
    unsigned int tile : 3;
    unsigned int palette : 4;
    unsigned int ct : 1;
    unsigned int mt : 1;
    unsigned int maskt : 4;
    unsigned int shiftt : 4;
    unsigned int cs : 1;
    unsigned int ms : 1;
    unsigned int masks : 4;
    unsigned int shifts : 4;
} Gsettile;

typedef struct {
    int cmd : 8;
    unsigned int sl : 12;
    unsigned int tl : 12;
    int pad : 5;
    unsigned int tile : 3;
    unsigned int sh : 12;
    unsigned int th : 12;
} Gloadtile;

typedef Gloadtile Gloadblock;

typedef Gloadtile Gsettilesize;

typedef Gloadtile Gloadtlut;

typedef struct {
    unsigned int cmd : 8;   /* command          */
    unsigned int xl : 12;   /* X coordinate of upper left   */
    unsigned int yl : 12;   /* Y coordinate of upper left   */
    unsigned int pad1 : 5;  /* Padding          */
    unsigned int tile : 3;  /* Tile descriptor index    */
    unsigned int xh : 12;   /* X coordinate of lower right  */
    unsigned int yh : 12;   /* Y coordinate of lower right  */
    unsigned int s : 16;    /* S texture coord at top left  */
    unsigned int t : 16;    /* T texture coord at top left  */
    unsigned int dsdx : 16; /* Change in S per change in X  */
    unsigned int dtdy : 16; /* Change in T per change in Y  */
} Gtexrect;

#define MakeTexRect(xh, yh, flip, tile, xl, yl, s, t, dsdx, dtdy) \
    G_TEXRECT, xh, yh, 0, flip, 0, tile, xl, yl, s, t, dsdx, dtdy

/*
 * Textured rectangles are 128 bits not 64 bits
 */
typedef struct {
    unsigned long w0;
    unsigned long w1;
    unsigned long w2;
    unsigned long w3;
} TexRect;

/*
 * Generic Gfx Packet
 */
typedef struct {
    uintptr_t w0;
    uintptr_t w1;

    // unsigned long long w0;
    // unsigned long long w1;
} Gwords;

#ifdef __cplusplus
static_assert(sizeof(Gwords) == 2 * sizeof(void*), "Display list size is bad");
#endif

/*
 * This union is the fundamental type of the display list.
 * It is, by law, exactly 64 bits in size.
 */
typedef union {
    Gwords words;
#if !defined(F3D_OLD) && IS_BIG_ENDIAN && !IS_64_BIT
    Gdma dma;
    Gtri tri;
    Gline3D line;
    Gpopmtx popmtx;
    Gsegment segment;
    GsetothermodeH setothermodeH;
    GsetothermodeL setothermodeL;
    Gtexture texture;
    Gperspnorm perspnorm;
    Gsetimg setimg;
    Gsetcombine setcombine;
    Gsetcolor setcolor;
    Gfillrect fillrect; /* use for setscissor also */
    Gsettile settile;
    Gloadtile loadtile; /* use for loadblock also, th is dxt */
    Gsettilesize settilesize;
    Gloadtlut loadtlut;
#endif
    long long int force_structure_alignment;
} Gfx;

/*
 * Macros to assemble the graphics display list
 */

/*
 * DMA macros
 */
#define gDma0p(pkt, c, s, l)                                      \
    _DW({                                                         \
        Gfx* _g = (Gfx*)(pkt);                                    \
                                                                  \
        _g->words.w0 = _SHIFTL((c), 24, 8) | _SHIFTL((l), 0, 24); \
        _g->words.w1 = (uintptr_t)(s);                            \
    })

#define gsDma0p(c, s, l) \
    { _SHIFTL((c), 24, 8) | _SHIFTL((l), 0, 24), (uintptr_t)(s) }

#define gDma1p(pkt, c, s, l, p)                                                           \
    _DW({                                                                                 \
        Gfx* _g = (Gfx*)(pkt);                                                            \
                                                                                          \
        _g->words.w0 = (_SHIFTL((c), 24, 8) | _SHIFTL((p), 16, 8) | _SHIFTL((l), 0, 16)); \
        _g->words.w1 = (uintptr_t)(s);                                                    \
    })

#define gsDma1p(c, s, l, p) \
    { (_SHIFTL((c), 24, 8) | _SHIFTL((p), 16, 8) | _SHIFTL((l), 0, 16)), (uintptr_t)(s) }

#define gDma2p(pkt, c, adrs, len, idx, ofs)                                                                          \
    _DW({                                                                                                            \
        Gfx* _g = (Gfx*)(pkt);                                                                                       \
        _g->words.w0 =                                                                                               \
            (_SHIFTL((c), 24, 8) | _SHIFTL(((len)-1) / 8, 19, 5) | _SHIFTL((ofs) / 8, 8, 8) | _SHIFTL((idx), 0, 8)); \
        _g->words.w1 = (uintptr_t)(adrs);                                                                            \
    })
#define gsDma2p(c, adrs, len, idx, ofs)                                                                          \
    {                                                                                                            \
        (_SHIFTL((c), 24, 8) | _SHIFTL(((len)-1) / 8, 19, 5) | _SHIFTL((ofs) / 8, 8, 8) | _SHIFTL((idx), 0, 8)), \
            (uintptr_t)(adrs)                                                                                    \
    }

#define gSPNoOp(pkt) gDma0p(pkt, G_SPNOOP, 0, 0)
#define gsSPNoOp() gsDma0p(G_SPNOOP, 0, 0)

#ifdef F3DEX_GBI_2
#define gSPMatrix(pkt, m, p) gDma2p((pkt), G_MTX, (m), sizeof(Mtx), (p) ^ G_MTX_PUSH, 0)
#define gsSPMatrix(m, p) gsDma2p(G_MTX, (m), sizeof(Mtx), (p) ^ G_MTX_PUSH, 0)
#else /* F3DEX_GBI_2 */
#define gSPMatrix(pkt, m, p) gDma1p(pkt, G_MTX, m, sizeof(Mtx), p)
#define gsSPMatrix(m, p) gsDma1p(G_MTX, m, sizeof(Mtx), p)
#endif /* F3DEX_GBI_2 */

#if defined(F3DEX_GBI_2)
/*
 * F3DEX_GBI_2: G_VTX GBI format was changed.
 *
 *        +--------+----+---+---+----+------+-+
 *  G_VTX |  cmd:8 |0000|  n:8  |0000|v0+n:7|0|
 *        +-+---+--+----+---+---+----+------+-+
 *        | |seg|         address             |
 *        +-+---+-----------------------------+
 */
#define __gSPVertex(pkt, v, n, v0)                                                              \
    _DW({                                                                                       \
        Gfx* _g = (Gfx*)(pkt);                                                                  \
        _g->words.w0 = _SHIFTL(G_VTX, 24, 8) | _SHIFTL((n), 12, 8) | _SHIFTL((v0) + (n), 1, 7); \
        _g->words.w1 = (uintptr_t)(v);                                                          \
    })
#define gsSPVertex(v, n, v0) \
    { (_SHIFTL(G_VTX, 24, 8) | _SHIFTL((n), 12, 8) | _SHIFTL((v0) + (n), 1, 7)), (uintptr_t)(v) }

#elif (defined(F3DEX_GBI) || defined(F3DLP_GBI))
/*
 * F3DEX_GBI: G_VTX GBI format was changed to support 64 vertice.
 *
 *        +--------+--------+------+----------+
 *  G_VTX |  cmd:8 |  v0:8  |  n:6 |length:10 |
 *        +-+---+--+--------+------+----------+
 *        | |seg|          address            |
 *        +-+---+-----------------------------+
 */
#define gSPVertex(pkt, v, n, v0) gDma1p((pkt), G_VTX, (v), ((n) << 10) | (sizeof(Vtx) * (n)-1), (v0)*2)
#define gsSPVertex(v, n, v0) gsDma1p(G_VTX, (v), ((n) << 10) | (sizeof(Vtx) * (n)-1), (v0)*2)
#else
#define gSPVertex(pkt, v, n, v0) gDma1p(pkt, G_VTX, v, sizeof(Vtx) * (n), ((n)-1) << 4 | (v0))
#define gsSPVertex(v, n, v0) gsDma1p(G_VTX, v, sizeof(Vtx) * (n), ((n)-1) << 4 | (v0))
#endif

#ifdef F3DEX_GBI_2
#define gSPViewport(pkt, v) gDma2p((pkt), G_MOVEMEM, (v), sizeof(Vp), G_MV_VIEWPORT, 0)
#define gsSPViewport(v) gsDma2p(G_MOVEMEM, (v), sizeof(Vp), G_MV_VIEWPORT, 0)
#else /* F3DEX_GBI_2 */
#define gSPViewport(pkt, v) gDma1p((pkt), G_MOVEMEM, (v), sizeof(Vp), G_MV_VIEWPORT)
#define gsSPViewport(v) gsDma1p(G_MOVEMEM, (v), sizeof(Vp), G_MV_VIEWPORT)
#endif /* F3DEX_GBI_2 */

#define gsSPPushCD(pkt, dl) gDma1p(pkt, G_PUSHCD, dl, 0, G_DL_PUSH)
#define __gSPDisplayList(pkt, dl) gDma1p(pkt, G_DL, dl, 0, G_DL_PUSH)
#define gsSPDisplayList(dl) gsDma1p(G_DL, dl, 0, G_DL_PUSH)
#define gsSPDisplayListOTRHash(dl) gsDma1p(G_DL_OTR_HASH, dl, 0, G_DL_PUSH)
#define gsSPDisplayListOTRFilePath(dl) gsDma1p(G_DL_OTR_FILEPATH, dl, 0, G_DL_PUSH)

#define gSPBranchList(pkt, dl) gDma1p(pkt, G_DL, dl, 0, G_DL_NOPUSH)
#define gsSPBranchList(dl) gsDma1p(G_DL, dl, 0, G_DL_NOPUSH)
#define gsSPBranchListOTRHash(dl) gsDma1p(G_DL_OTR_HASH, dl, 0, G_DL_NOPUSH)
#define gsSPBranchListOTRFilePath(dl) gsDma1p(G_DL_OTR_FILEPATH, dl, 0, G_DL_NOPUSH)

#define gSPSprite2DBase(pkt, s) gDma1p(pkt, G_SPRITE2D_BASE, s, sizeof(uSprite), 0)
#define gsSPSprite2DBase(s) gsDma1p(G_SPRITE2D_BASE, s, sizeof(uSprite), 0)

/*
 * RSP short command (no DMA required) macros
 */
#define gImmp0(pkt, c)                      \
    _DW({                                   \
        Gfx* _g = (Gfx*)(pkt);              \
                                            \
        _g->words.w0 = _SHIFTL((c), 24, 8); \
    })

#define gsImmp0(c) \
    { _SHIFTL((c), 24, 8) }

#define gImmp1(pkt, c, p0)                  \
    _DW({                                   \
        Gfx* _g = (Gfx*)(pkt);              \
                                            \
        _g->words.w0 = _SHIFTL((c), 24, 8); \
        _g->words.w1 = (uintptr_t)(p0);     \
    })

#define gsImmp1(c, p0) \
    { _SHIFTL((c), 24, 8), (uintptr_t)(p0) }

#define gImmp2(pkt, c, p0, p1)                                      \
    _DW({                                                           \
        Gfx* _g = (Gfx*)(pkt);                                      \
                                                                    \
        _g->words.w0 = _SHIFTL((c), 24, 8);                         \
        _g->words.w1 = _SHIFTL((p0), 16, 16) | _SHIFTL((p1), 8, 8); \
    })

#define gsImmp2(c, p0, p1) \
    { _SHIFTL((c), 24, 8), _SHIFTL((p0), 16, 16) | _SHIFTL((p1), 8, 8) }

#define gImmp3(pkt, c, p0, p1, p2)                                                          \
    _DW({                                                                                   \
        Gfx* _g = (Gfx*)(pkt);                                                              \
                                                                                            \
        _g->words.w0 = _SHIFTL((c), 24, 8);                                                 \
        _g->words.w1 = (_SHIFTL((p0), 16, 16) | _SHIFTL((p1), 8, 8) | _SHIFTL((p2), 0, 8)); \
    })

#define gsImmp3(c, p0, p1, p2) \
    { _SHIFTL((c), 24, 8), (_SHIFTL((p0), 16, 16) | _SHIFTL((p1), 8, 8) | _SHIFTL((p2), 0, 8)) }

#define gImmp21(pkt, c, p0, p1, dat)                                                       \
    _DW({                                                                                  \
        Gfx* _g = (Gfx*)(pkt);                                                             \
                                                                                           \
        _g->words.w0 = (_SHIFTL((c), 24, 8) | _SHIFTL((p0), 8, 16) | _SHIFTL((p1), 0, 8)); \
        _g->words.w1 = (uintptr_t)(dat);                                                   \
    })

#define gsImmp21(c, p0, p1, dat) \
    { _SHIFTL((c), 24, 8) | _SHIFTL((p0), 8, 16) | _SHIFTL((p1), 0, 8), (uintptr_t)(dat) }

#ifdef F3DEX_GBI_2
#define gMoveWd(pkt, index, offset, data) gDma1p((pkt), G_MOVEWORD, data, offset, index)
#define gsMoveWd(index, offset, data) gsDma1p(G_MOVEWORD, data, offset, index)
#else /* F3DEX_GBI_2 */
#define gMoveWd(pkt, index, offset, data) gImmp21((pkt), G_MOVEWORD, offset, index, data)
#define gsMoveWd(index, offset, data) gsImmp21(G_MOVEWORD, offset, index, data)
#endif /* F3DEX_GBI_2 */

/* Sprite immediate macros, there is also a sprite dma macro above */

#define gSPSprite2DScaleFlip(pkt, sx, sy, fx, fy)                                                          \
    _DW({                                                                                                  \
        Gfx* _g = (Gfx*)(pkt);                                                                             \
                                                                                                           \
        _g->words.w0 = (_SHIFTL(G_SPRITE2D_SCALEFLIP, 24, 8) | _SHIFTL((fx), 8, 8) | _SHIFTL((fy), 0, 8)); \
        _g->words.w1 = (_SHIFTL((sx), 16, 16) | _SHIFTL((sy), 0, 16));                                     \
    })

#define gsSPSprite2DScaleFlip(sx, sy, fx, fy)                                               \
    {                                                                                       \
        (_SHIFTL(G_SPRITE2D_SCALEFLIP, 24, 8) | _SHIFTL((fx), 8, 8) | _SHIFTL((fy), 0, 8)), \
            (_SHIFTL((sx), 16, 16) | _SHIFTL((sy), 0, 16))                                  \
    }

#define gSPSprite2DDraw(pkt, px, py)                                   \
    _DW({                                                              \
        Gfx* _g = (Gfx*)(pkt);                                         \
                                                                       \
        _g->words.w0 = (_SHIFTL(G_SPRITE2D_DRAW, 24, 8));              \
        _g->words.w1 = (_SHIFTL((px), 16, 16) | _SHIFTL((py), 0, 16)); \
    })

#define gsSPSprite2DDraw(px, py) \
    { (_SHIFTL(G_SPRITE2D_DRAW, 24, 8)), (_SHIFTL((px), 16, 16) | _SHIFTL((py), 0, 16)) }

/*
 * Note: the SP1Triangle() and line macros multiply the vertex indices
 * by 10, this is an optimization for the microcode.
 */
#if (defined(F3DLP_GBI) || defined(F3DEX_GBI))
#define __gsSP1Triangle_w1(v0, v1, v2) (_SHIFTL((v0)*2, 16, 8) | _SHIFTL((v1)*2, 8, 8) | _SHIFTL((v2)*2, 0, 8))
#define __gsSP1Triangle_w1f(v0, v1, v2, flag)         \
    (((flag) == 0)   ? __gsSP1Triangle_w1(v0, v1, v2) \
     : ((flag) == 1) ? __gsSP1Triangle_w1(v1, v2, v0) \
                     : __gsSP1Triangle_w1(v2, v0, v1))
#define __gsSPLine3D_w1(v0, v1, wd) (_SHIFTL((v0)*2, 16, 8) | _SHIFT((v1)*2, 8, 8) | _SHIFT((wd), 0, 8))
#define __gsSPLine3D_w1f(v0, v1, wd, flag) (((flag) == 0) ? __gsSPLine3D_w1(v0, v1, wd) : __gsSPLine3D_w1(v1, v0, wd))
#define __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag)   \
    (((flag) == 0)   ? __gsSP1Triangle_w1(v0, v1, v2) \
     : ((flag) == 1) ? __gsSP1Triangle_w1(v1, v2, v3) \
     : ((flag) == 2) ? __gsSP1Triangle_w1(v2, v3, v0) \
                     : __gsSP1Triangle_w1(v3, v0, v1))
#define __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag)   \
    (((flag) == 0)   ? __gsSP1Triangle_w1(v0, v2, v3) \
     : ((flag) == 1) ? __gsSP1Triangle_w1(v1, v3, v0) \
     : ((flag) == 2) ? __gsSP1Triangle_w1(v2, v0, v1) \
                     : __gsSP1Triangle_w1(v3, v1, v2))
#else
#define __gsSP1Triangle_w1f(v0, v1, v2, flag) \
    (_SHIFTL((flag), 24, 8) | _SHIFTL((v0)*10, 16, 8) | _SHIFTL((v1)*10, 8, 8) | _SHIFTL((v2)*10, 0, 8))
#define __gsSPLine3D_w1f(v0, v1, wd, flag) \
    (_SHIFTL((flag), 24, 8) | _SHIFTL((v0)*10, 16, 8) | _SHIFTL((v1)*10, 8, 8) | _SHIFTL((wd), 0, 8))
#endif

#ifdef F3DEX_GBI_2
/***
 ***  1 Triangle
 ***/
#define gSP1Triangle(pkt, v0, v1, v2, flag)                                            \
    _DW({                                                                              \
        Gfx* _g = (Gfx*)(pkt);                                                         \
                                                                                       \
        _g->words.w0 = _SHIFTL(G_TRI1, 24, 8) | __gsSP1Triangle_w1f(v0, v1, v2, flag); \
        _g->words.w1 = 0;                                                              \
    })
#define gsSP1Triangle(v0, v1, v2, flag) \
    { _SHIFTL(G_TRI1, 24, 8) | __gsSP1Triangle_w1f(v0, v1, v2, flag), 0 }

#define gsSP1TriangleOTR(v0, v1, v2, flag) \
    { _SHIFTL(G_TRI1_OTR, 24, 8) | __gsSP1Triangle_w1f(v0, v1, v2, flag), 0 }

/***
 ***  Line
 ***/
#define gSPLine3D(pkt, v0, v1, flag)                                                 \
    _DW({                                                                            \
        Gfx* _g = (Gfx*)(pkt);                                                       \
                                                                                     \
        _g->words.w0 = _SHIFTL(G_LINE3D, 24, 8) | __gsSPLine3D_w1f(v0, v1, 0, flag); \
        _g->words.w1 = 0;                                                            \
    })
#define gsSPLine3D(v0, v1, flag) \
    { _SHIFTL(G_LINE3D, 24, 8) | __gsSPLine3D_w1f(v0, v1, 0, flag), 0 }

/***
 ***  LineW
 ***/
/* these macros are the same as SPLine3D, except they have an
 * additional parameter for width. The width is added to the "minimum"
 * thickness, which is 1.5 pixels. The units for width are in
 * half-pixel units, so a width of 1 translates to (.5 + 1.5) or
 * a 2.0 pixels wide line.
 */
#define gSPLineW3D(pkt, v0, v1, wd, flag)                                             \
    _DW({                                                                             \
        Gfx* _g = (Gfx*)(pkt);                                                        \
                                                                                      \
        _g->words.w0 = _SHIFTL(G_LINE3D, 24, 8) | __gsSPLine3D_w1f(v0, v1, wd, flag); \
        _g->words.w1 = 0;                                                             \
    })
#define gsSPLineW3D(v0, v1, wd, flag) \
    { _SHIFTL(G_LINE3D, 24, 8) | __gsSPLine3D_w1f(v0, v1, wd, flag), 0 }

/***
 ***  1 Quadrangle
 ***/
#define gSP1Quadrangle(pkt, v0, v1, v2, v3, flag)                                              \
    _DW({                                                                                      \
        Gfx* _g = (Gfx*)(pkt);                                                                 \
                                                                                               \
        _g->words.w0 = (_SHIFTL(G_QUAD, 24, 8) | __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag)); \
        _g->words.w1 = __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag);                            \
    })

#define gsSP1Quadrangle(v0, v1, v2, v3, flag)                                   \
    {                                                                           \
        (_SHIFTL(G_QUAD, 24, 8) | __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag)), \
            __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag)                         \
    }
#else /* F3DEX_GBI_2 */

/***
 ***  1 Triangle
 ***/
#define gSP1Triangle(pkt, v0, v1, v2, flag)                   \
    {                                                         \
        Gfx* _g = (Gfx*)(pkt);                                \
                                                              \
        _g->words.w0 = _SHIFTL(G_TRI1, 24, 8);                \
        _g->words.w1 = __gsSP1Triangle_w1f(v0, v1, v2, flag); \
    }
#define gsSP1Triangle(v0, v1, v2, flag) \
    { _SHIFTL(G_TRI1, 24, 8), __gsSP1Triangle_w1f(v0, v1, v2, flag) }

/***
 ***  Line
 ***/
#define gSPLine3D(pkt, v0, v1, flag)                      \
    {                                                     \
        Gfx* _g = (Gfx*)(pkt);                            \
                                                          \
        _g->words.w0 = _SHIFTL(G_LINE3D, 24, 8);          \
        _g->words.w1 = __gsSPLine3D_w1f(v0, v1, 0, flag); \
    }
#define gsSPLine3D(v0, v1, flag) \
    { _SHIFTL(G_LINE3D, 24, 8), __gsSPLine3D_w1f(v0, v1, 0, flag) }

/***
 ***  LineW
 ***/
/* these macros are the same as SPLine3D, except they have an
 * additional parameter for width. The width is added to the "minimum"
 * thickness, which is 1.5 pixels. The units for width are in
 * half-pixel units, so a width of 1 translates to (.5 + 1.5) or
 * a 2.0 pixels wide line.
 */
#define gSPLineW3D(pkt, v0, v1, wd, flag)                  \
    {                                                      \
        Gfx* _g = (Gfx*)(pkt);                             \
                                                           \
        _g->words.w0 = _SHIFTL(G_LINE3D, 24, 8);           \
        _g->words.w1 = __gsSPLine3D_w1f(v0, v1, wd, flag); \
    }
#define gsSPLineW3D(v0, v1, wd, flag) \
    { _SHIFTL(G_LINE3D, 24, 8), __gsSPLine3D_w1f(v0, v1, wd, flag) }

/***
 ***  1 Quadrangle
 ***/
#define gSP1Quadrangle(pkt, v0, v1, v2, v3, flag)                                              \
    {                                                                                          \
        Gfx* _g = (Gfx*)(pkt);                                                                 \
                                                                                               \
        _g->words.w0 = (_SHIFTL(G_TRI2, 24, 8) | __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag)); \
        _g->words.w1 = __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag);                            \
    }

#define gsSP1Quadrangle(v0, v1, v2, v3, flag)                                   \
    {                                                                           \
        (_SHIFTL(G_TRI2, 24, 8) | __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag)), \
            __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag)                         \
    }
#endif /* F3DEX_GBI_2 */

#if (defined(F3DLP_GBI) || defined(F3DEX_GBI))
/***
 ***  2 Triangles
 ***/
#define gSP2Triangles(pkt, v00, v01, v02, flag0, v10, v11, v12, flag1)                       \
    _DW({                                                                                    \
        Gfx* _g = (Gfx*)(pkt);                                                               \
                                                                                             \
        _g->words.w0 = (_SHIFTL(G_TRI2, 24, 8) | __gsSP1Triangle_w1f(v00, v01, v02, flag0)); \
        _g->words.w1 = __gsSP1Triangle_w1f(v10, v11, v12, flag1);                            \
    })

#define gsSP2Triangles(v00, v01, v02, flag0, v10, v11, v12, flag1) \
    { (_SHIFTL(G_TRI2, 24, 8) | __gsSP1Triangle_w1f(v00, v01, v02, flag0)), __gsSP1Triangle_w1f(v10, v11, v12, flag1) }

#endif /* F3DEX_GBI/F3DLP_GBI */

#if (defined(F3DEX_GBI) || defined(F3DLP_GBI))
#define gSPCullDisplayList(pkt, vstart, vend)                                 \
    _DW({                                                                     \
        Gfx* _g = (Gfx*)(pkt);                                                \
                                                                              \
        _g->words.w0 = _SHIFTL(G_CULLDL, 24, 8) | _SHIFTL((vstart)*2, 0, 16); \
        _g->words.w1 = _SHIFTL((vend)*2, 0, 16);                              \
    })

#define gsSPCullDisplayList(vstart, vend) \
    { _SHIFTL(G_CULLDL, 24, 8) | _SHIFTL((vstart)*2, 0, 16), _SHIFTL((vend)*2, 0, 16) }

#else
#define gSPCullDisplayList(pkt, vstart, vend)                               \
    {                                                                       \
        Gfx* _g = (Gfx*)(pkt);                                              \
                                                                            \
        _g->words.w0 = _SHIFTL(G_CULLDL, 24, 8) | ((0x0f & (vstart)) * 40); \
        _g->words.w1 = (unsigned int)((0x0f & ((vend) + 1)) * 40);          \
    }

#define gsSPCullDisplayList(vstart, vend) \
    { _SHIFTL(G_CULLDL, 24, 8) | ((0x0f & (vstart)) * 40), ((0x0f & ((vend) + 1)) * 40) }
#endif

#define __gSPSegment(pkt, segment, base) gMoveWd(pkt, G_MW_SEGMENT, (segment)*4, base)
#define gsSPSegment(segment, base) gsMoveWd(G_MW_SEGMENT, (segment)*4, base)

/*
 * Clipping Macros
 */
#define FR_NEG_FRUSTRATIO_1 0x00000001
#define FR_POS_FRUSTRATIO_1 0x0000ffff
#define FR_NEG_FRUSTRATIO_2 0x00000002
#define FR_POS_FRUSTRATIO_2 0x0000fffe
#define FR_NEG_FRUSTRATIO_3 0x00000003
#define FR_POS_FRUSTRATIO_3 0x0000fffd
#define FR_NEG_FRUSTRATIO_4 0x00000004
#define FR_POS_FRUSTRATIO_4 0x0000fffc
#define FR_NEG_FRUSTRATIO_5 0x00000005
#define FR_POS_FRUSTRATIO_5 0x0000fffb
#define FR_NEG_FRUSTRATIO_6 0x00000006
#define FR_POS_FRUSTRATIO_6 0x0000fffa
/*
 * r should be one of: FRUSTRATIO_1, FRUSTRATIO_2, FRUSTRATIO_3, ... FRUSTRATIO_6
 */
#define gSPClipRatio(pkt, r)                                 \
    _DW({                                                    \
        gMoveWd(pkt, G_MW_CLIP, G_MWO_CLIP_RNX, FR_NEG_##r); \
        gMoveWd(pkt, G_MW_CLIP, G_MWO_CLIP_RNY, FR_NEG_##r); \
        gMoveWd(pkt, G_MW_CLIP, G_MWO_CLIP_RPX, FR_POS_##r); \
        gMoveWd(pkt, G_MW_CLIP, G_MWO_CLIP_RPY, FR_POS_##r); \
    })

#define gsSPClipRatio(r)                                                                              \
    gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RNX, FR_NEG_##r), gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RNY, FR_NEG_##r), \
        gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RPX, FR_POS_##r), gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RPY, FR_POS_##r)

/*
 * Insert values into Matrix
 *
 * where = element of matrix (byte offset)
 * num   = new element (32 bit value replacing 2 int or 2 frac matrix
 *                                 componants
 */
#ifdef F3DEX_GBI_2
#define gSPInsertMatrix(pkt, where, num) ERROR !!gSPInsertMatrix is no longer supported.
#define gsSPInsertMatrix(where, num) ERROR !!gsSPInsertMatrix is no longer supported.
#else
#define gSPInsertMatrix(pkt, where, num) gMoveWd(pkt, G_MW_MATRIX, where, num)
#define gsSPInsertMatrix(where, num) gsMoveWd(G_MW_MATRIX, where, num)
#endif

/*
 * Load new matrix directly
 *
 * mptr = pointer to matrix
 */
#ifdef F3DEX_GBI_2
#define gSPForceMatrix(pkt, mptr)                                      \
    _DW({                                                              \
        gDma2p((pkt), G_MOVEMEM, (mptr), sizeof(Mtx), G_MV_MATRIX, 0); \
        gMoveWd((pkt), G_MW_FORCEMTX, 0, 0x00010000);                  \
    })
#define gsSPForceMatrix(mptr) \
    gsDma2p(G_MOVEMEM, (mptr), sizeof(Mtx), G_MV_MATRIX, 0), gsMoveWd(G_MW_FORCEMTX, 0, 0x00010000)

#else /* F3DEX_GBI_2 */
#define gSPForceMatrix(pkt, mptr)                                      \
    {                                                                  \
        gDma1p(pkt, G_MOVEMEM, mptr, 16, G_MV_MATRIX_1);               \
        gDma1p(pkt, G_MOVEMEM, (char*)(mptr) + 16, 16, G_MV_MATRIX_2); \
        gDma1p(pkt, G_MOVEMEM, (char*)(mptr) + 32, 16, G_MV_MATRIX_3); \
        gDma1p(pkt, G_MOVEMEM, (char*)(mptr) + 48, 16, G_MV_MATRIX_4); \
    }
#define gsSPForceMatrix(mptr)                                                                               \
    gsDma1p(G_MOVEMEM, mptr, 16, G_MV_MATRIX_1), gsDma1p(G_MOVEMEM, (char*)(mptr) + 16, 16, G_MV_MATRIX_2), \
        gsDma1p(G_MOVEMEM, (char*)(mptr) + 32, 16, G_MV_MATRIX_3),                                          \
        gsDma1p(G_MOVEMEM, (char*)(mptr) + 48, 16, G_MV_MATRIX_4)
#endif /* F3DEX_GBI_2 */

/*
 * Insert values into Points
 *
 * point = point number 0-15
 * where = which element of point to modify (byte offset into point)
 * num   = new value (32 bit)
 */
#if (defined(F3DEX_GBI) || defined(F3DLP_GBI))
#define gSPModifyVertex(pkt, vtx, where, val)                                                             \
    _DW({                                                                                                 \
        Gfx* _g = (Gfx*)(pkt);                                                                            \
        _g->words.w0 = (_SHIFTL(G_MODIFYVTX, 24, 8) | _SHIFTL((where), 16, 8) | _SHIFTL((vtx)*2, 0, 16)); \
        _g->words.w1 = (unsigned int)(val);                                                               \
    })
#define gsSPModifyVertex(vtx, where, val) \
    { _SHIFTL(G_MODIFYVTX, 24, 8) | _SHIFTL((where), 16, 8) | _SHIFTL((vtx)*2, 0, 16), (unsigned int)(val) }
#else
#define gSPModifyVertex(pkt, vtx, where, val) gMoveWd(pkt, G_MW_POINTS, (vtx)*40 + (where), val)
#define gsSPModifyVertex(vtx, where, val) gsMoveWd(G_MW_POINTS, (vtx)*40 + (where), val)
#endif

#if (defined(F3DEX_GBI) || defined(F3DLP_GBI))
/*
 *  gSPBranchLessZ   Branch DL if (vtx.z) less than or equal (zval).
 *
 *  dl   = DL branch to
 *  vtx  = Vertex
 *  zval = Screen depth
 *  near = Near plane
 *  far  = Far  plane
 *  flag = G_BZ_PERSP or G_BZ_ORTHO
 */

#define G_BZ_PERSP 0
#define G_BZ_ORTHO 1

#define G_DEPTOZSrg(zval, near, far, flag, zmin, zmax)                                                             \
    (((unsigned int)FTOFIX32(((flag) == G_BZ_PERSP                                                                 \
                                  ? (1.0f - (float)(near) / (float)(zval)) / (1.0f - (float)(near) / (float)(far)) \
                                  : ((float)(zval) - (float)(near)) / ((float)(far) - (float)(near))))) *          \
         (((int)((zmax) - (zmin))) & ~1) +                                                                         \
     (int)FTOFIX32(zmin))

#define G_DEPTOZS(zval, near, far, flag) G_DEPTOZSrg(zval, near, far, flag, 0, G_MAXZ)

#define gSPBranchLessZrg(pkt, dl, vtx, zval, near, far, flag, zmin, zmax)                                 \
    _DW({                                                                                                 \
        Gfx* _g = (Gfx*)(pkt);                                                                            \
        _g->words.w0 = _SHIFTL(G_RDPHALF_1, 24, 8);                                                       \
        _g->words.w1 = (uintptr_t)(dl);                                                                   \
        _g = (Gfx*)(pkt);                                                                                 \
        _g->words.w0 = (_SHIFTL(G_BRANCH_Z, 24, 8) | _SHIFTL((vtx)*5, 12, 12) | _SHIFTL((vtx)*2, 0, 12)); \
        _g->words.w1 = G_DEPTOZSrg(zval, near, far, flag, zmin, zmax);                                    \
    })

#define gsSPBranchLessZrg(dl, vtx, zval, near, far, flag, zmin, zmax)                    \
    {                                                                                    \
        _SHIFTL(G_RDPHALF_1, 24, 8),                                                     \
        (uintptr_t)(dl),                                                                 \
    },                                                                                   \
    {                                                                                    \
        _SHIFTL(G_BRANCH_Z, 24, 8) | _SHIFTL((vtx)*5, 12, 12) | _SHIFTL((vtx)*2, 0, 12), \
            G_DEPTOZSrg(zval, near, far, flag, zmin, zmax),                              \
    }

#define gSPBranchLessZ(pkt, dl, vtx, zval, near, far, flag) \
    gSPBranchLessZrg(pkt, dl, vtx, zval, near, far, flag, 0, G_MAXZ)
#define gsSPBranchLessZ(dl, vtx, zval, near, far, flag) gsSPBranchLessZrg(dl, vtx, zval, near, far, flag, 0, G_MAXZ)

/*
 *  gSPBranchLessZraw   Branch DL if (vtx.z) less than or equal (raw zval).
 *
 *  dl   = DL branch to
 *  vtx  = Vertex
 *  zval = Raw value of screen depth
 */
#define gSPBranchLessZraw(pkt, dl, vtx, zval)                                                             \
    _DW({                                                                                                 \
        Gfx* _g = (Gfx*)(pkt);                                                                            \
        _g->words.w0 = _SHIFTL(G_RDPHALF_1, 24, 8);                                                       \
        _g->words.w1 = (uintptr_t)(dl);                                                                   \
        _g = (Gfx*)(pkt);                                                                                 \
        _g->words.w0 = (_SHIFTL(G_BRANCH_Z, 24, 8) | _SHIFTL((vtx)*5, 12, 12) | _SHIFTL((vtx)*2, 0, 12)); \
        _g->words.w1 = (uintptr_t)(zval);                                                                 \
    })

#define gsSPBranchLessZraw(dl, vtx, zval)                                                                   \
    {                                                                                                       \
        _SHIFTL(G_RDPHALF_1, 24, 8),                                                                        \
        (uintptr_t)(dl),                                                                                    \
    },                                                                                                      \
    {                                                                                                       \
        _SHIFTL(G_BRANCH_Z, 24, 8) | _SHIFTL((vtx)*5, 12, 12) | _SHIFTL((vtx)*2, 0, 12), (uintptr_t)(zval), \
    }

/*
 * gSPLoadUcode   RSP loads specified ucode.
 *
 * uc_start  = ucode text section start
 * uc_dstart = ucode data section start
 */
#define gSPLoadUcodeEx(pkt, uc_start, uc_dstart, uc_dsize)                                 \
    _DW({                                                                                  \
        Gfx* _g = (Gfx*)(pkt);                                                             \
        _g->words.w0 = _SHIFTL(G_RDPHALF_1, 24, 8);                                        \
        _g->words.w1 = (uintptr_t)(uc_dstart);                                             \
        _g = (Gfx*)(pkt);                                                                  \
        _g->words.w0 = (_SHIFTL(G_LOAD_UCODE, 24, 8) | _SHIFTL((int)(uc_dsize)-1, 0, 16)); \
        _g->words.w1 = (uintptr_t)(uc_start);                                              \
    })

#define gsSPLoadUcodeEx(uc_start, uc_dstart, uc_dsize)                                           \
    {                                                                                            \
        _SHIFTL(G_RDPHALF_1, 24, 8),                                                             \
        (uintptr_t)(uc_dstart),                                                                  \
    },                                                                                           \
    {                                                                                            \
        _SHIFTL(G_LOAD_UCODE, 24, 8) | _SHIFTL((int)(uc_dsize)-1, 0, 16), (uintptr_t)(uc_start), \
    }

#define gSPLoadUcode(pkt, uc_start, uc_dstart) gSPLoadUcodeEx((pkt), (uc_start), (uc_dstart), SP_UCODE_DATA_SIZE)
#define gsSPLoadUcode(uc_start, uc_dstart) gsSPLoadUcodeEx((uc_start), (uc_dstart), SP_UCODE_DATA_SIZE)

#define gSPLoadUcodeL(pkt, ucode) \
    gSPLoadUcode((pkt), OS_K0_TO_PHYSICAL(&##ucode##TextStart), OS_K0_TO_PHYSICAL(&##ucode##DataStart))
#define gsSPLoadUcodeL(ucode) \
    gsSPLoadUcode(OS_K0_TO_PHYSICAL(&##ucode##TextStart), OS_K0_TO_PHYSICAL(&##ucode##DataStart))
#endif

#ifdef F3DEX_GBI_2
/*
 * gSPDma_io  DMA to/from DMEM/IMEM for DEBUG.
 */
#define gSPDma_io(pkt, flag, dmem, dram, size)                                                           \
    _DW({                                                                                                \
        Gfx* _g = (Gfx*)(pkt);                                                                           \
        _g->words.w0 = _SHIFTL(G_DMA_IO, 24, 8) | _SHIFTL((flag), 23, 1) | _SHIFTL((dmem) / 8, 13, 10) | \
                       _SHIFTL((size)-1, 0, 12);                                                         \
        _g->words.w1 = (uintptr_t)(dram);                                                                \
    })

#define gsSPDma_io(flag, dmem, dram, size)                                                                          \
    {                                                                                                               \
        _SHIFTL(G_DMA_IO, 24, 8) | _SHIFTL((flag), 23, 1) | _SHIFTL((dmem) / 8, 13, 10) | _SHIFTL((size)-1, 0, 12), \
            (uintptr_t)(dram)                                                                                       \
    }

#define gSPDmaRead(pkt, dmem, dram, size) gSPDma_io((pkt), 0, (dmem), (dram), (size))
#define gsSPDmaRead(dmem, dram, size) gsSPDma_io(0, (dmem), (dram), (size))
#define gSPDmaWrite(pkt, dmem, dram, size) gSPDma_io((pkt), 1, (dmem), (dram), (size))
#define gsSPDmaWrite(dmem, dram, size) gsSPDma_io(1, (dmem), (dram), (size))
#endif

/*
 * Lighting Macros
 */
#ifdef F3DEX_GBI_2
#define NUML(n) ((n)*24)
#else
#define NUML(n) (((n) + 1) * 32 + 0x80000000)
#endif
#define NUMLIGHTS_0 1
#define NUMLIGHTS_1 1
#define NUMLIGHTS_2 2
#define NUMLIGHTS_3 3
#define NUMLIGHTS_4 4
#define NUMLIGHTS_5 5
#define NUMLIGHTS_6 6
#define NUMLIGHTS_7 7
/*
 * n should be one of: NUMLIGHTS_0, NUMLIGHTS_1, ..., NUMLIGHTS_7
 * NOTE: in addition to the number of directional lights specified,
 *       there is always 1 ambient light
 */
#define gSPNumLights(pkt, n) gMoveWd(pkt, G_MW_NUMLIGHT, G_MWO_NUMLIGHT, NUML(n))
#define gsSPNumLights(n) gsMoveWd(G_MW_NUMLIGHT, G_MWO_NUMLIGHT, NUML(n))

#define LIGHT_1 1
#define LIGHT_2 2
#define LIGHT_3 3
#define LIGHT_4 4
#define LIGHT_5 5
#define LIGHT_6 6
#define LIGHT_7 7
#define LIGHT_8 8
/*
 * l should point to a Light struct
 * n should be one of: LIGHT_1, LIGHT_2, ..., LIGHT_8
 * NOTE: the highest numbered light is always the ambient light (eg if there are
 *       3 directional lights defined: gsSPNumLights(NUMLIGHTS_3), then lights
 *       LIGHT_1 through LIGHT_3 will be the directional lights and light
 *       LIGHT_4 will be the ambient light.
 */
#ifdef F3DEX_GBI_2
#define gSPLight(pkt, l, n) gDma2p((pkt), G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, (n)*24 + 24)
#define gsSPLight(l, n) gsDma2p(G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, (n)*24 + 24)
#else /* F3DEX_GBI_2 */
#define gSPLight(pkt, l, n) gDma1p(pkt, G_MOVEMEM, l, sizeof(Light), ((n)-1) * 2 + G_MV_L0)
#define gsSPLight(l, n) gsDma1p(G_MOVEMEM, l, sizeof(Light), ((n)-1) * 2 + G_MV_L0)
#endif /* F3DEX_GBI_2 */

/*
 * gSPLightColor changes color of light without recalculating light direction
 * col is a 32 bit word with r,g,b,a (alpha is ignored)
 * n should be one of LIGHT_1, LIGHT_2, ..., LIGHT_8
 */
#define gSPLightColor(pkt, n, col)                    \
    _DW({                                             \
        gMoveWd(pkt, G_MW_LIGHTCOL, G_MWO_a##n, col); \
        gMoveWd(pkt, G_MW_LIGHTCOL, G_MWO_b##n, col); \
    })
#define gsSPLightColor(n, col) gsMoveWd(G_MW_LIGHTCOL, G_MWO_a##n, col), gsMoveWd(G_MW_LIGHTCOL, G_MWO_b##n, col)

/* These macros use a structure "name" which is init'd with the gdSPDefLights macros*/

#define gSPSetLights0(pkt, name)        \
    _DW({                               \
        gSPNumLights(pkt, NUMLIGHTS_0); \
        gSPLight(pkt, &name.l[0], 1);   \
        gSPLight(pkt, &name.a, 2);      \
    })
#define gsSPSetLights0(name) gsSPNumLights(NUMLIGHTS_0), gsSPLight(&name.l[0], 1), gsSPLight(&name.a, 2)

#define gSPSetLights1(pkt, name)        \
    _DW({                               \
        gSPNumLights(pkt, NUMLIGHTS_1); \
        gSPLight(pkt, &name.l[0], 1);   \
        gSPLight(pkt, &name.a, 2);      \
    })
#define gsSPSetLights1(name) gsSPNumLights(NUMLIGHTS_1), gsSPLight(&name.l[0], 1), gsSPLight(&name.a, 2)

#define gSPSetLights2(pkt, name)        \
    _DW({                               \
        gSPNumLights(pkt, NUMLIGHTS_2); \
        gSPLight(pkt, &name.l[0], 1);   \
        gSPLight(pkt, &name.l[1], 2);   \
        gSPLight(pkt, &name.a, 3);      \
    })
#define gsSPSetLights2(name) \
    gsSPNumLights(NUMLIGHTS_2), gsSPLight(&name.l[0], 1), gsSPLight(&name.l[1], 2), gsSPLight(&name.a, 3)

#define gSPSetLights3(pkt, name)        \
    _DW({                               \
        gSPNumLights(pkt, NUMLIGHTS_3); \
        gSPLight(pkt, &name.l[0], 1);   \
        gSPLight(pkt, &name.l[1], 2);   \
        gSPLight(pkt, &name.l[2], 3);   \
        gSPLight(pkt, &name.a, 4);      \
    })
#define gsSPSetLights3(name)                                                                                  \
    gsSPNumLights(NUMLIGHTS_3), gsSPLight(&name.l[0], 1), gsSPLight(&name.l[1], 2), gsSPLight(&name.l[2], 3), \
        gsSPLight(&name.a, 4)

#define gSPSetLights4(pkt, name)        \
    _DW({                               \
        gSPNumLights(pkt, NUMLIGHTS_4); \
        gSPLight(pkt, &name.l[0], 1);   \
        gSPLight(pkt, &name.l[1], 2);   \
        gSPLight(pkt, &name.l[2], 3);   \
        gSPLight(pkt, &name.l[3], 4);   \
        gSPLight(pkt, &name.a, 5);      \
    })
#define gsSPSetLights4(name)                                                                                  \
    gsSPNumLights(NUMLIGHTS_4), gsSPLight(&name.l[0], 1), gsSPLight(&name.l[1], 2), gsSPLight(&name.l[2], 3), \
        gsSPLight(&name.l[3], 4), gsSPLight(&name.a, 5)

#define gSPSetLights5(pkt, name)        \
    _DW({                               \
        gSPNumLights(pkt, NUMLIGHTS_5); \
        gSPLight(pkt, &name.l[0], 1);   \
        gSPLight(pkt, &name.l[1], 2);   \
        gSPLight(pkt, &name.l[2], 3);   \
        gSPLight(pkt, &name.l[3], 4);   \
        gSPLight(pkt, &name.l[4], 5);   \
        gSPLight(pkt, &name.a, 6);      \
    })

#define gsSPSetLights5(name)                                                                                  \
    gsSPNumLights(NUMLIGHTS_5), gsSPLight(&name.l[0], 1), gsSPLight(&name.l[1], 2), gsSPLight(&name.l[2], 3), \
        gsSPLight(&name.l[3], 4), gsSPLight(&name.l[4], 5), gsSPLight(&name.a, 6)

#define gSPSetLights6(pkt, name)        \
    _DW({                               \
        gSPNumLights(pkt, NUMLIGHTS_6); \
        gSPLight(pkt, &name.l[0], 1);   \
        gSPLight(pkt, &name.l[1], 2);   \
        gSPLight(pkt, &name.l[2], 3);   \
        gSPLight(pkt, &name.l[3], 4);   \
        gSPLight(pkt, &name.l[4], 5);   \
        gSPLight(pkt, &name.l[5], 6);   \
        gSPLight(pkt, &name.a, 7);      \
    })

#define gsSPSetLights6(name)                                                                                  \
    gsSPNumLights(NUMLIGHTS_6), gsSPLight(&name.l[0], 1), gsSPLight(&name.l[1], 2), gsSPLight(&name.l[2], 3), \
        gsSPLight(&name.l[3], 4), gsSPLight(&name.l[4], 5), gsSPLight(&name.l[5], 6), gsSPLight(&name.a, 7)

#define gSPSetLights7(pkt, name)        \
    _DW({                               \
        gSPNumLights(pkt, NUMLIGHTS_7); \
        gSPLight(pkt, &name.l[0], 1);   \
        gSPLight(pkt, &name.l[1], 2);   \
        gSPLight(pkt, &name.l[2], 3);   \
        gSPLight(pkt, &name.l[3], 4);   \
        gSPLight(pkt, &name.l[4], 5);   \
        gSPLight(pkt, &name.l[5], 6);   \
        gSPLight(pkt, &name.l[6], 7);   \
        gSPLight(pkt, &name.a, 8);      \
    })

#define gsSPSetLights7(name)                                                                                    \
    gsSPNumLights(NUMLIGHTS_7), gsSPLight(&name.l[0], 1), gsSPLight(&name.l[1], 2), gsSPLight(&name.l[2], 3),   \
        gsSPLight(&name.l[3], 4), gsSPLight(&name.l[4], 5), gsSPLight(&name.l[5], 6), gsSPLight(&name.l[6], 7), \
        gsSPLight(&name.a, 8)

/*
 * Reflection/Hiliting Macros
 */
#ifdef F3DEX_GBI_2
#define gSPLookAtX(pkt, l) gDma2p((pkt), G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, G_MVO_LOOKATX)
#define gsSPLookAtX(l) gsDma2p(G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, G_MVO_LOOKATX)
#define gSPLookAtY(pkt, l) gDma2p((pkt), G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, G_MVO_LOOKATY)
#define gsSPLookAtY(l) gsDma2p(G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, G_MVO_LOOKATY)
#else /* F3DEX_GBI_2 */
#define gSPLookAtX(pkt, l) gDma1p(pkt, G_MOVEMEM, l, sizeof(Light), G_MV_LOOKATX)
#define gsSPLookAtX(l) gsDma1p(G_MOVEMEM, l, sizeof(Light), G_MV_LOOKATX)
#define gSPLookAtY(pkt, l) gDma1p(pkt, G_MOVEMEM, l, sizeof(Light), G_MV_LOOKATY)
#define gsSPLookAtY(l) gsDma1p(G_MOVEMEM, l, sizeof(Light), G_MV_LOOKATY)
#endif /* F3DEX_GBI_2 */

#define gSPLookAt(pkt, la)                 \
    _DW({                                  \
        gSPLookAtX(pkt, la);               \
        gSPLookAtY(pkt, (char*)(la) + 16); \
    })
#define gsSPLookAt(la) gsSPLookAtX(la), gsSPLookAtY((char*)(la) + 16)

#define gDPSetHilite1Tile(pkt, tile, hilite, width, height)                   \
    gDPSetTileSize(pkt, tile, (hilite)->h.x1 & 0xfff, (hilite)->h.y1 & 0xfff, \
                   ((((width)-1) * 4) + (hilite)->h.x1) & 0xfff, ((((height)-1) * 4) + (hilite)->h.y1) & 0xfff)
#define gsDPSetHilite1Tile(tile, hilite, width, height)                   \
    gsDPSetTileSize(tile, (hilite)->h.x1 & 0xfff, (hilite)->h.y1 & 0xfff, \
                    ((((width)-1) * 4) + (hilite)->h.x1) & 0xfff, ((((height)-1) * 4) + (hilite)->h.y1) & 0xfff)

#define gDPSetHilite2Tile(pkt, tile, hilite, width, height)                   \
    gDPSetTileSize(pkt, tile, (hilite)->h.x2 & 0xfff, (hilite)->h.y2 & 0xfff, \
                   ((((width)-1) * 4) + (hilite)->h.x2) & 0xfff, ((((height)-1) * 4) + (hilite)->h.y2) & 0xfff)
#define gsDPSetHilite2Tile(tile, hilite, width, height)                   \
    gsDPSetTileSize(tile, (hilite)->h.x2 & 0xfff, (hilite)->h.y2 & 0xfff, \
                    ((((width)-1) * 4) + (hilite)->h.x2) & 0xfff, ((((height)-1) * 4) + (hilite)->h.y2) & 0xfff)

/*
 * FOG macros
 * fm = z multiplier
 * fo = z offset
 * FOG FORMULA:    alpha(fog) = (eyespace z) * fm  + fo  CLAMPED 0 to 255
 *   note: (eyespace z) ranges -1 to 1
 *
 * Alternate method of setting fog:
 * min, max: range 0 to 1000: 0=nearplane, 1000=farplane
 * min is where fog begins (usually less than max and often 0)
 * max is where fog is thickest (usually 1000)
 *
 */
#define gSPFogFactor(pkt, fm, fo) gMoveWd(pkt, G_MW_FOG, G_MWO_FOG, (_SHIFTL(fm, 16, 16) | _SHIFTL(fo, 0, 16)))

#define gsSPFogFactor(fm, fo) gsMoveWd(G_MW_FOG, G_MWO_FOG, (_SHIFTL(fm, 16, 16) | _SHIFTL(fo, 0, 16)))

#define gSPFogPosition(pkt, min, max) \
    gMoveWd(pkt, G_MW_FOG, G_MWO_FOG, \
            (_SHIFTL((128000 / ((max) - (min))), 16, 16) | _SHIFTL(((500 - (min)) * 256 / ((max) - (min))), 0, 16)))

#define gsSPFogPosition(min, max) \
    gsMoveWd(G_MW_FOG, G_MWO_FOG, \
             (_SHIFTL((128000 / ((max) - (min))), 16, 16) | _SHIFTL(((500 - (min)) * 256 / ((max) - (min))), 0, 16)))

#ifdef F3DEX_GBI_2
/*
 * Macros to turn texture on/off
 */
#define gSPTexture(pkt, s, t, level, tile, on)                                                             \
    _DW({                                                                                                  \
        Gfx* _g = (Gfx*)(pkt);                                                                             \
                                                                                                           \
        _g->words.w0 = (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL(BOWTIE_VAL, 16, 8) | _SHIFTL((level), 11, 3) | \
                        _SHIFTL((tile), 8, 3) | _SHIFTL((on), 1, 7));                                      \
        _g->words.w1 = (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16));                                       \
    })
#define gsSPTexture(s, t, level, tile, on)                                                                          \
    {                                                                                                               \
        (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL(BOWTIE_VAL, 16, 8) | _SHIFTL((level), 11, 3) | _SHIFTL((tile), 8, 3) | \
         _SHIFTL((on), 1, 7)),                                                                                      \
            (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16))                                                            \
    }
/*
 * Different version of SPTexture macro, has an additional parameter
 * which is currently reserved in the microcode.
 */
#define gSPTextureL(pkt, s, t, level, xparam, tile, on)                                                  \
    _DW({                                                                                                \
        Gfx* _g = (Gfx*)(pkt);                                                                           \
                                                                                                         \
        _g->words.w0 = (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL((xparam), 16, 8) | _SHIFTL((level), 11, 3) | \
                        _SHIFTL((tile), 8, 3) | _SHIFTL((on), 1, 7));                                    \
        _g->words.w1 = (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16));                                     \
    })
#define gsSPTextureL(s, t, level, xparam, tile, on)                                                               \
    {                                                                                                             \
        (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL((xparam), 16, 8) | _SHIFTL((level), 11, 3) | _SHIFTL((tile), 8, 3) | \
         _SHIFTL((on), 1, 7)),                                                                                    \
            (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16))                                                          \
    }
#else
/*
 * Macros to turn texture on/off
 */
#define gSPTexture(pkt, s, t, level, tile, on)                                                             \
    {                                                                                                      \
        Gfx* _g = (Gfx*)(pkt);                                                                             \
                                                                                                           \
        _g->words.w0 = (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL(BOWTIE_VAL, 16, 8) | _SHIFTL((level), 11, 3) | \
                        _SHIFTL((tile), 8, 3) | _SHIFTL((on), 0, 8));                                      \
        _g->words.w1 = (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16));                                       \
    }
#define gsSPTexture(s, t, level, tile, on)                                                                          \
    {                                                                                                               \
        (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL(BOWTIE_VAL, 16, 8) | _SHIFTL((level), 11, 3) | _SHIFTL((tile), 8, 3) | \
         _SHIFTL((on), 0, 8)),                                                                                      \
            (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16))                                                            \
    }
/*
 * Different version of SPTexture macro, has an additional parameter
 * which is currently reserved in the microcode.
 */
#define gSPTextureL(pkt, s, t, level, xparam, tile, on)                                                  \
    {                                                                                                    \
        Gfx* _g = (Gfx*)(pkt);                                                                           \
                                                                                                         \
        _g->words.w0 = (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL((xparam), 16, 8) | _SHIFTL((level), 11, 3) | \
                        _SHIFTL((tile), 8, 3) | _SHIFTL((on), 0, 8));                                    \
        _g->words.w1 = (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16));                                     \
    }
#define gsSPTextureL(s, t, level, xparam, tile, on)                                                               \
    {                                                                                                             \
        (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL((xparam), 16, 8) | _SHIFTL((level), 11, 3) | _SHIFTL((tile), 8, 3) | \
         _SHIFTL((on), 0, 8)),                                                                                    \
            (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16))                                                          \
    }
#endif

#define gSPPerspNormalize(pkt, s) gMoveWd(pkt, G_MW_PERSPNORM, 0, (s))
#define gsSPPerspNormalize(s) gsMoveWd(G_MW_PERSPNORM, 0, (s))

#ifdef F3DEX_GBI_2
#define gSPPopMatrixN(pkt, n, num) gDma2p((pkt), G_POPMTX, (num)*64, 64, 2, 0)
#define gsSPPopMatrixN(n, num) gsDma2p(G_POPMTX, (num)*64, 64, 2, 0)
#define gSPPopMatrix(pkt, n) gSPPopMatrixN((pkt), (n), 1)
#define gsSPPopMatrix(n) gsSPPopMatrixN((n), 1)
#else /* F3DEX_GBI_2 */
#define gSPPopMatrix(pkt, n) gImmp1(pkt, G_POPMTX, n)
#define gsSPPopMatrix(n) gsImmp1(G_POPMTX, n)
#endif /* F3DEX_GBI_2 */

#define gSPEndDisplayList(pkt)                  \
    _DW({                                       \
        Gfx* _g = (Gfx*)(pkt);                  \
                                                \
        _g->words.w0 = _SHIFTL(G_ENDDL, 24, 8); \
        _g->words.w1 = 0;                       \
    })

#define gsSPEndDisplayList() \
    { _SHIFTL(G_ENDDL, 24, 8), 0 }

#define __gSPInvalidateTexCache(pkt, addr)              \
    _DW({                                               \
        Gfx* _g = (Gfx*)(pkt);                          \
                                                        \
        _g->words.w0 = _SHIFTL(G_INVALTEXCACHE, 24, 8); \
        _g->words.w1 = addr;                            \
    })

#define gsSPInvalidateTexCache() \
    { _SHIFTL(G_INVALTEXCACHE, 24, 8), 0 }

#define gsSPSetFB(pkt, fb)                      \
    {                                           \
        Gfx* _g = (Gfx*)(pkt);                  \
                                                \
        _g->words.w0 = _SHIFTL(G_SETFB, 24, 8); \
        _g->words.w1 = fb;                      \
    }

#define gsSPResetFB(pkt)                          \
    {                                             \
        Gfx* _g = (Gfx*)(pkt);                    \
                                                  \
        _g->words.w0 = _SHIFTL(G_RESETFB, 24, 8); \
        _g->words.w1 = 0;                         \
    }

#define gSPGrayscale(pkt, state)                       \
    {                                                  \
        Gfx* _g = (Gfx*)(pkt);                         \
                                                       \
        _g->words.w0 = _SHIFTL(G_SETGRAYSCALE, 24, 8); \
        _g->words.w1 = state;                          \
    }

#define gsSPGrayscale(state) \
    { (_SHIFTL(G_SETGRAYSCALE, 24, 8)), (state) }

#define gSPExtraGeometryMode(pkt, c, s)                                                 \
    _DW({                                                                               \
        Gfx* _g = (Gfx*)(pkt);                                                          \
                                                                                        \
        _g->words.w0 = _SHIFTL(G_EXTRAGEOMETRYMODE, 24, 8) | _SHIFTL(~(u32)(c), 0, 24); \
        _g->words.w1 = (u32)(s);                                                        \
    })

#define gSPSetExtraGeometryMode(pkt, word) gSPExtraGeometryMode((pkt), 0, word)
#define gSPClearExtraGeometryMode(pkt, word) gSPExtraGeometryMode((pkt), word, 0)

#ifdef F3DEX_GBI_2
/*
 *  One gSPGeometryMode(pkt,c,s) GBI is equal to these two GBIs.
 *
 *      gSPClearGeometryMode(pkt,c)
 *      gSPSetGeometryMode(pkt,s)
 *
 *  gSPLoadGeometryMode(pkt, word) sets GeometryMode directly.
 */
#define gSPGeometryMode(pkt, c, s)                                                 \
    _DW({                                                                          \
        Gfx* _g = (Gfx*)(pkt);                                                     \
        _g->words.w0 = _SHIFTL(G_GEOMETRYMODE, 24, 8) | _SHIFTL(~(u32)(c), 0, 24); \
        _g->words.w1 = (u32)(s);                                                   \
    })

#define gsSPGeometryMode(c, s) \
    { (_SHIFTL(G_GEOMETRYMODE, 24, 8) | _SHIFTL(~(u32)(c), 0, 24)), (u32)(s) }
#define gSPSetGeometryMode(pkt, word) gSPGeometryMode((pkt), 0, (word))
#define gsSPSetGeometryMode(word) gsSPGeometryMode(0, (word))
#define gSPClearGeometryMode(pkt, word) gSPGeometryMode((pkt), (word), 0)
#define gsSPClearGeometryMode(word) gsSPGeometryMode((word), 0)
#define gSPLoadGeometryMode(pkt, word) gSPGeometryMode((pkt), -1, (word))
#define gsSPLoadGeometryMode(word) gsSPGeometryMode(-1, (word))

#else /* F3DEX_GBI_2 */
#define gSPSetGeometryMode(pkt, word)                     \
    {                                                     \
        Gfx* _g = (Gfx*)(pkt);                            \
                                                          \
        _g->words.w0 = _SHIFTL(G_SETGEOMETRYMODE, 24, 8); \
        _g->words.w1 = (unsigned int)(word);              \
    }

#define gsSPSetGeometryMode(word) \
    { _SHIFTL(G_SETGEOMETRYMODE, 24, 8), (unsigned int)(word) }

#define gSPClearGeometryMode(pkt, word)                     \
    {                                                       \
        Gfx* _g = (Gfx*)(pkt);                              \
                                                            \
        _g->words.w0 = _SHIFTL(G_CLEARGEOMETRYMODE, 24, 8); \
        _g->words.w1 = (unsigned int)(word);                \
    }

#define gsSPClearGeometryMode(word) \
    { _SHIFTL(G_CLEARGEOMETRYMODE, 24, 8), (unsigned int)(word) }
#endif /* F3DEX_GBI_2 */

#ifdef F3DEX_GBI_2
#define gSPSetOtherMode(pkt, cmd, sft, len, data)                                                          \
    _DW({                                                                                                  \
        Gfx* _g = (Gfx*)(pkt);                                                                             \
        _g->words.w0 = (_SHIFTL(cmd, 24, 8) | _SHIFTL(32 - (sft) - (len), 8, 8) | _SHIFTL((len)-1, 0, 8)); \
        _g->words.w1 = (unsigned int)(data);                                                               \
    })

#define gsSPSetOtherMode(cmd, sft, len, data) \
    { _SHIFTL(cmd, 24, 8) | _SHIFTL(32 - (sft) - (len), 8, 8) | _SHIFTL((len)-1, 0, 8), (unsigned int)(data) }
#else
#define gSPSetOtherMode(pkt, cmd, sft, len, data)                                       \
    {                                                                                   \
        Gfx* _g = (Gfx*)(pkt);                                                          \
                                                                                        \
        _g->words.w0 = (_SHIFTL(cmd, 24, 8) | _SHIFTL(sft, 8, 8) | _SHIFTL(len, 0, 8)); \
        _g->words.w1 = (unsigned int)(data);                                            \
    }

#define gsSPSetOtherMode(cmd, sft, len, data) \
    { _SHIFTL(cmd, 24, 8) | _SHIFTL(sft, 8, 8) | _SHIFTL(len, 0, 8), (unsigned int)(data) }
#endif

/*
 * RDP setothermode register commands - register shadowed in RSP
 */
#define gDPPipelineMode(pkt, mode) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_PIPELINE, 1, mode)
#define gsDPPipelineMode(mode) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_PIPELINE, 1, mode)

#define gDPSetCycleType(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_CYCLETYPE, 2, type)
#define gsDPSetCycleType(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_CYCLETYPE, 2, type)

#define gDPSetTexturePersp(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTPERSP, 1, type)
#define gsDPSetTexturePersp(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTPERSP, 1, type)

#define gDPSetTextureDetail(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTDETAIL, 2, type)
#define gsDPSetTextureDetail(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTDETAIL, 2, type)

#define gDPSetTextureLOD(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTLOD, 1, type)
#define gsDPSetTextureLOD(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTLOD, 1, type)

#define gDPSetTextureLUT(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTLUT, 2, type)
#define gsDPSetTextureLUT(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTLUT, 2, type)

#define gDPSetTextureFilter(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTFILT, 2, type)
#define gsDPSetTextureFilter(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTFILT, 2, type)

#define gDPSetTextureConvert(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTCONV, 3, type)
#define gsDPSetTextureConvert(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTCONV, 3, type)

#define gDPSetCombineKey(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_COMBKEY, 1, type)
#define gsDPSetCombineKey(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_COMBKEY, 1, type)

#ifndef _HW_VERSION_1
#define gDPSetColorDither(pkt, mode) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_RGBDITHER, 2, mode)
#define gsDPSetColorDither(mode) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_RGBDITHER, 2, mode)
#else
#define gDPSetColorDither(pkt, mode) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_COLORDITHER, 1, mode)
#define gsDPSetColorDither(mode) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_COLORDITHER, 1, mode)
#endif

#ifndef _HW_VERSION_1
#define gDPSetAlphaDither(pkt, mode) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_ALPHADITHER, 2, mode)
#define gsDPSetAlphaDither(mode) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_ALPHADITHER, 2, mode)
#endif

#define gDPSetDither(pkt, mode) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_ALPHADITHER, 4, mode)

/* 'blendmask' is not supported anymore.
 * The bits are reserved for future use.
 * Fri May 26 13:45:55 PDT 1995
 */
#define gDPSetBlendMask(pkt, mask) gDPNoOp(pkt)
#define gsDPSetBlendMask(mask) gsDPNoOp()

#define gDPSetAlphaCompare(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_L, G_MDSFT_ALPHACOMPARE, 2, type)
#define gsDPSetAlphaCompare(type) gsSPSetOtherMode(G_SETOTHERMODE_L, G_MDSFT_ALPHACOMPARE, 2, type)

#define gDPSetDepthSource(pkt, src) gSPSetOtherMode(pkt, G_SETOTHERMODE_L, G_MDSFT_ZSRCSEL, 1, src)
#define gsDPSetDepthSource(src) gsSPSetOtherMode(G_SETOTHERMODE_L, G_MDSFT_ZSRCSEL, 1, src)

#define gDPSetRenderMode(pkt, c0, c1) gSPSetOtherMode(pkt, G_SETOTHERMODE_L, G_MDSFT_RENDERMODE, 29, (c0) | (c1))
#define gsDPSetRenderMode(c0, c1) gsSPSetOtherMode(G_SETOTHERMODE_L, G_MDSFT_RENDERMODE, 29, (c0) | (c1))

#define gSetImage(pkt, cmd, fmt, siz, width, i)                                                                     \
    _DW({                                                                                                           \
        Gfx* _g = (Gfx*)(pkt);                                                                                      \
                                                                                                                    \
        _g->words.w0 = _SHIFTL(cmd, 24, 8) | _SHIFTL(fmt, 21, 3) | _SHIFTL(siz, 19, 2) | _SHIFTL((width)-1, 0, 12); \
        _g->words.w1 = (uintptr_t)(i);                                                                              \
    })

#define gsSetImage(cmd, fmt, siz, width, i) \
    { _SHIFTL(cmd, 24, 8) | _SHIFTL(fmt, 21, 3) | _SHIFTL(siz, 19, 2) | _SHIFTL((width)-1, 0, 12), (uintptr_t)(i) }

#define gDPSetColorImage(pkt, f, s, w, i) gSetImage(pkt, G_SETCIMG, f, s, w, i)
#define gsDPSetColorImage(f, s, w, i) gsSetImage(G_SETCIMG, f, s, w, i)

/* use these for new code */
#define gDPSetDepthImage(pkt, i) gSetImage(pkt, G_SETZIMG, 0, 0, 1, i)
#define gsDPSetDepthImage(i) gsSetImage(G_SETZIMG, 0, 0, 1, i)
/* kept for compatibility */
#define gDPSetMaskImage(pkt, i) gDPSetDepthImage(pkt, i)
#define gsDPSetMaskImage(i) gsDPSetDepthImage(i)

#define __gDPSetTextureImage(pkt, f, s, w, i) gSetImage(pkt, G_SETTIMG, f, s, w, i)
#define gsDPSetTextureImage(f, s, w, i) gsSetImage(G_SETTIMG, f, s, w, i)
#define __gDPSetTextureImageFB(pkt, f, s, w, i) gSetImage(pkt, G_SETTIMG_FB, f, s, w, i)

/*
 * RDP macros
 */

#define gDPSetCombine(pkt, muxs0, muxs1)                                     \
    _DW({                                                                    \
        Gfx* _g = (Gfx*)(pkt);                                               \
                                                                             \
        _g->words.w0 = _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(muxs0, 0, 24); \
        _g->words.w1 = (unsigned int)(muxs1);                                \
    })

#define gsDPSetCombine(muxs0, muxs1) \
    { _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(muxs0, 0, 24), (unsigned int)(muxs1) }

#define GCCc0w0(saRGB0, mRGB0, saA0, mA0) \
    (_SHIFTL((saRGB0), 20, 4) | _SHIFTL((mRGB0), 15, 5) | _SHIFTL((saA0), 12, 3) | _SHIFTL((mA0), 9, 3))

#define GCCc1w0(saRGB1, mRGB1) (_SHIFTL((saRGB1), 5, 4) | _SHIFTL((mRGB1), 0, 5))

#define GCCc0w1(sbRGB0, aRGB0, sbA0, aA0) \
    (_SHIFTL((sbRGB0), 28, 4) | _SHIFTL((aRGB0), 15, 3) | _SHIFTL((sbA0), 12, 3) | _SHIFTL((aA0), 9, 3))

#define GCCc1w1(sbRGB1, saA1, mA1, aRGB1, sbA1, aA1)                                                      \
    (_SHIFTL((sbRGB1), 24, 4) | _SHIFTL((saA1), 21, 3) | _SHIFTL((mA1), 18, 3) | _SHIFTL((aRGB1), 6, 3) | \
     _SHIFTL((sbA1), 3, 3) | _SHIFTL((aA1), 0, 3))

#define gDPSetCombineLERP(pkt, a0, b0, c0, d0, Aa0, Ab0, Ac0, Ad0, a1, b1, c1, d1, Aa1, Ab1, Ac1, Ad1)                 \
    _DW({                                                                                                              \
        Gfx* _g = (Gfx*)(pkt);                                                                                         \
                                                                                                                       \
        _g->words.w0 =                                                                                                 \
            _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(GCCc0w0(G_CCMUX_##a0, G_CCMUX_##c0, G_ACMUX_##Aa0, G_ACMUX_##Ac0) | \
                                                       GCCc1w0(G_CCMUX_##a1, G_CCMUX_##c1),                            \
                                                   0, 24);                                                             \
        _g->words.w1 = (unsigned int)(GCCc0w1(G_CCMUX_##b0, G_CCMUX_##d0, G_ACMUX_##Ab0, G_ACMUX_##Ad0) |              \
                                      GCCc1w1(G_CCMUX_##b1, G_ACMUX_##Aa1, G_ACMUX_##Ac1, G_CCMUX_##d1, G_ACMUX_##Ab1, \
                                              G_ACMUX_##Ad1));                                                         \
    })

#define gsDPSetCombineLERP(a0, b0, c0, d0, Aa0, Ab0, Ac0, Ad0, a1, b1, c1, d1, Aa1, Ab1, Ac1, Ad1)                 \
    {                                                                                                              \
        _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(GCCc0w0(G_CCMUX_##a0, G_CCMUX_##c0, G_ACMUX_##Aa0, G_ACMUX_##Ac0) | \
                                                   GCCc1w0(G_CCMUX_##a1, G_CCMUX_##c1),                            \
                                               0, 24),                                                             \
            (unsigned int)(GCCc0w1(G_CCMUX_##b0, G_CCMUX_##d0, G_ACMUX_##Ab0, G_ACMUX_##Ad0) |                     \
                           GCCc1w1(G_CCMUX_##b1, G_ACMUX_##Aa1, G_ACMUX_##Ac1, G_CCMUX_##d1, G_ACMUX_##Ab1,        \
                                   G_ACMUX_##Ad1))                                                                 \
    }

#define gsDPSetCombineLERP_NoMacros(a0, b0, c0, d0, Aa0, Ab0, Ac0, Ad0, a1, b1, c1, d1, Aa1, Ab1, Ac1, Ad1) \
    {                                                                                                       \
        _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(GCCc0w0(a0, c0, Aa0, Ac0) | GCCc1w0(a1, c1), 0, 24),         \
            (unsigned int)(GCCc0w1(b0, d0, Ab0, Ad0) | GCCc1w1(b1, Aa1, Ac1, d1, Ab1, Ad1))                 \
    }

/*
 * SetCombineMode macros are NOT redunant. It allow the C preprocessor
 * to substitute single parameter which includes commas in the token and
 * rescan for higher parameter count macro substitution.
 *
 * eg.  gsDPSetCombineMode(G_CC_MODULATE, G_CC_MODULATE) turns into
 *  gsDPSetCombineLERP(TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0,
 *      TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0)
 */

/*
#if _MSC_VER
#define gDPSetCombineMode(pkt, a, b)	gDPNoParam(pkt, G_NOOP)
#else
#define gDPSetCombineMode(pkt, a, b)    gDPSetCombineLERP(pkt, a, b)
//#define gDPSetCombineMode(pkt, a, b)    gDPSetCombineLERP(pkt, ##a, b)
#endif
*/

#if defined(_MSC_VER)
#define CALL_2(A, B) A B
#define CALL_3(A, B, C) A B C

#define gDPSetCombineMode(pkt, a, b) CALL_2(gDPSetCombineLERP, (pkt, a, b))
#define gsDPSetCombineMode(a, b) CALL_2(gsDPSetCombineLERP, (a, b))
#else
#define gDPSetCombineMode(pkt, a, b) gDPSetCombineLERP(pkt, a, b)
#define gsDPSetCombineMode(a, b) gsDPSetCombineLERP(a, b)
#endif

#if defined(_MSC_VER) || defined(__GNUC__)
#define CALL_2(A, B) A B
#define CALL_3(A, B, C) A B C

// #define	gsDPSetCombineMode(a, b)	CALL_2(gsDPSetCombineLERP, (a, b))
//   #define gsDPSetCombineMode(a, b)    _SHIFTL(0, 24, 8), 0
#else
#define gsDPSetCombineMode(a, b) gsDPSetCombineLERP(a, b)
#endif

#define gDPSetColor(pkt, c, d)            \
    _DW({                                 \
        Gfx* _g = (Gfx*)(pkt);            \
                                          \
        _g->words.w0 = _SHIFTL(c, 24, 8); \
        _g->words.w1 = (unsigned int)(d); \
    })

#define gsDPSetColor(c, d) \
    { _SHIFTL(c, 24, 8), (unsigned int)(d) }

#define DPRGBColor(pkt, cmd, r, g, b, a) \
    gDPSetColor(pkt, cmd, (_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | _SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8)))
#define sDPRGBColor(cmd, r, g, b, a) \
    gsDPSetColor(cmd, (_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | _SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8)))

#define gDPSetGrayscaleColor(pkt, r, g, b, lerp) DPRGBColor(pkt, G_SETINTENSITY, r, g, b, lerp)
#define gsDPSetGrayscaleColor(r, g, b, a) sDPRGBColor(G_SETINTENSITY, r, g, b, a)
#define gDPSetEnvColor(pkt, r, g, b, a) DPRGBColor(pkt, G_SETENVCOLOR, r, g, b, a)
#define gsDPSetEnvColor(r, g, b, a) sDPRGBColor(G_SETENVCOLOR, r, g, b, a)
#define gDPSetBlendColor(pkt, r, g, b, a) DPRGBColor(pkt, G_SETBLENDCOLOR, r, g, b, a)
#define gsDPSetBlendColor(r, g, b, a) sDPRGBColor(G_SETBLENDCOLOR, r, g, b, a)
#define gDPSetFogColor(pkt, r, g, b, a) DPRGBColor(pkt, G_SETFOGCOLOR, r, g, b, a)
#define gsDPSetFogColor(r, g, b, a) sDPRGBColor(G_SETFOGCOLOR, r, g, b, a)
#define gDPSetFillColor(pkt, d) gDPSetColor(pkt, G_SETFILLCOLOR, (d))
#define gsDPSetFillColor(d) gsDPSetColor(G_SETFILLCOLOR, (d))
#define gDPSetPrimDepth(pkt, z, dz) gDPSetColor(pkt, G_SETPRIMDEPTH, _SHIFTL(z, 16, 16) | _SHIFTL(dz, 0, 16))
#define gsDPSetPrimDepth(z, dz) gsDPSetColor(G_SETPRIMDEPTH, _SHIFTL(z, 16, 16) | _SHIFTL(dz, 0, 16))

#define gDPSetPrimColor(pkt, m, l, r, g, b, a)                                                        \
    _DW({                                                                                             \
        Gfx* _g = (Gfx*)(pkt);                                                                        \
                                                                                                      \
        _g->words.w0 = (_SHIFTL(G_SETPRIMCOLOR, 24, 8) | _SHIFTL(m, 8, 8) | _SHIFTL(l, 0, 8));        \
        _g->words.w1 = (_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | _SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8)); \
    })

#define gsDPSetPrimColor(m, l, r, g, b, a)                                                \
    {                                                                                     \
        (_SHIFTL(G_SETPRIMCOLOR, 24, 8) | _SHIFTL(m, 8, 8) | _SHIFTL(l, 0, 8)),           \
            (_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | _SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8)) \
    }

/*
 * gDPSetOtherMode (This is for expert user.)
 *
 * This command makes all othermode parameters set.
 * Do not use this command in the same DL with another g*SPSetOtherMode DLs.
 *
 * [Usage]
 *  gDPSetOtherMode(pkt, modeA, modeB)
 *
 *      'modeA' is described all parameters of GroupA GBI command.
 *      'modeB' is also described all parameters of GroupB GBI command.
 *
 *  GroupA:
 *    gDPPipelineMode, gDPSetCycleType, gSPSetTexturePersp,
 *    gDPSetTextureDetail, gDPSetTextureLOD, gDPSetTextureLUT,
 *    gDPSetTextureFilter, gDPSetTextureConvert, gDPSetCombineKey,
 *    gDPSetColorDither, gDPSetAlphaDither
 *
 *  GroupB:
 *    gDPSetAlphaCompare, gDPSetDepthSource, gDPSetRenderMode
 *
 *  Use 'OR' operation to get modeA and modeB.
 *
 *  modeA = G_PM_* | G_CYC_* | G_TP_* | G_TD_* | G_TL_* | G_TT_* | G_TF_*
 *      G_TC_* | G_CK_*  | G_CD_* | G_AD_*;
 *
 *  modeB = G_AC_* | G_ZS_*  | G_RM_* | G_RM_*2;
 */
#define gDPSetOtherMode(pkt, mode0, mode1)                                        \
    _DW({                                                                         \
        Gfx* _g = (Gfx*)(pkt);                                                    \
                                                                                  \
        _g->words.w0 = _SHIFTL(G_RDPSETOTHERMODE, 24, 8) | _SHIFTL(mode0, 0, 24); \
        _g->words.w1 = (unsigned int)(mode1);                                     \
    })

#define gsDPSetOtherMode(mode0, mode1) \
    { _SHIFTL(G_RDPSETOTHERMODE, 24, 8) | _SHIFTL(mode0, 0, 24), (unsigned int)(mode1) }

/*
 * Texturing macros
 */

/* These are also defined defined above for Sprite Microcode */

#define G_TX_LOADTILE 7
#define G_TX_RENDERTILE 0

#define G_TX_NOMIRROR 0
#define G_TX_WRAP 0
#define G_TX_MIRROR 0x1
#define G_TX_CLAMP 0x2
#define G_TX_NOMASK 0
#define G_TX_NOLOD 0

#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif

#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
/*
 *  Dxt is the inverse of the number of 64-bit words in a line of
 *  the texture being loaded using the load_block command.  If
 *  there are any 1's to the right of the 11th fractional bit,
 *  dxt should be rounded up.  The following macros accomplish
 *  this.  The 4b macros are a special case since 4-bit textures
 *  are loaded as 8-bit textures.  Dxt is fixed point 1.11. RJM
 */
#define G_TX_DXT_FRAC 11

/*
 *  For RCP 2.0, the maximum number of texels that can be loaded
 *  using a load_block command is 2048.  In order to load the total
 *  4kB of Tmem, change the texel size when loading to be G_IM_SIZ_16b,
 *  then change the tile to the proper texel size after the load.
 *  The g*DPLoadTextureBlock macros already do this, so this change
 *  will be transparent if you use these macros.  If you use
 *  the g*DPLoadBlock macros directly, you will need to handle this
 *  tile manipulation yourself.  RJM.
 */

#define G_TX_LDBLK_MAX_TXL 4095

#define TXL2WORDS(txls, b_txl) MAX(1, ((txls) * (b_txl) / 8))
#define CALC_DXT(width, b_txl) (((1 << G_TX_DXT_FRAC) + TXL2WORDS(width, b_txl) - 1) / TXL2WORDS(width, b_txl))

#define TXL2WORDS_4b(txls) MAX(1, ((txls) / 16))
#define CALC_DXT_4b(width) (((1 << G_TX_DXT_FRAC) + TXL2WORDS_4b(width) - 1) / TXL2WORDS_4b(width))

#define gDPLoadTileGeneric(pkt, c, tile, uls, ult, lrs, lrt)                              \
    _DW({                                                                                 \
        Gfx* _g = (Gfx*)(pkt);                                                            \
                                                                                          \
        _g->words.w0 = _SHIFTL(c, 24, 8) | _SHIFTL(uls, 12, 12) | _SHIFTL(ult, 0, 12);    \
        _g->words.w1 = _SHIFTL(tile, 24, 3) | _SHIFTL(lrs, 12, 12) | _SHIFTL(lrt, 0, 12); \
    })

#define gsDPLoadTileGeneric(c, tile, uls, ult, lrs, lrt)                      \
    {                                                                         \
        _SHIFTL(c, 24, 8) | _SHIFTL(uls, 12, 12) | _SHIFTL(ult, 0, 12),       \
            _SHIFTL(tile, 24, 3) | _SHIFTL(lrs, 12, 12) | _SHIFTL(lrt, 0, 12) \
    }

#define gDPSetTileSize(pkt, t, uls, ult, lrs, lrt) gDPLoadTileGeneric(pkt, G_SETTILESIZE, t, uls, ult, lrs, lrt)
#define gsDPSetTileSize(t, uls, ult, lrs, lrt) gsDPLoadTileGeneric(G_SETTILESIZE, t, uls, ult, lrs, lrt)
#define gDPLoadTile(pkt, t, uls, ult, lrs, lrt) gDPLoadTileGeneric(pkt, G_LOADTILE, t, uls, ult, lrs, lrt)
#define gsDPLoadTile(t, uls, ult, lrs, lrt) gsDPLoadTileGeneric(G_LOADTILE, t, uls, ult, lrs, lrt)

#define gDPSetTile(pkt, fmt, siz, line, tmem, tile, palette, cmt, maskt, shiftt, cms, masks, shifts)                  \
    _DW({                                                                                                             \
        Gfx* _g = (Gfx*)(pkt);                                                                                        \
                                                                                                                      \
        _g->words.w0 = _SHIFTL(G_SETTILE, 24, 8) | _SHIFTL(fmt, 21, 3) | _SHIFTL(siz, 19, 2) | _SHIFTL(line, 9, 9) |  \
                       _SHIFTL(tmem, 0, 9);                                                                           \
        _g->words.w1 = _SHIFTL(tile, 24, 3) | _SHIFTL(palette, 20, 4) | _SHIFTL(cmt, 18, 2) | _SHIFTL(maskt, 14, 4) | \
                       _SHIFTL(shiftt, 10, 4) | _SHIFTL(cms, 8, 2) | _SHIFTL(masks, 4, 4) | _SHIFTL(shifts, 0, 4);    \
    })

#define gsDPSetTile(fmt, siz, line, tmem, tile, palette, cmt, maskt, shiftt, cms, masks, shifts)            \
    {                                                                                                       \
        (_SHIFTL(G_SETTILE, 24, 8) | _SHIFTL(fmt, 21, 3) | _SHIFTL(siz, 19, 2) | _SHIFTL(line, 9, 9) |      \
         _SHIFTL(tmem, 0, 9)),                                                                              \
            (_SHIFTL(tile, 24, 3) | _SHIFTL(palette, 20, 4) | _SHIFTL(cmt, 18, 2) | _SHIFTL(maskt, 14, 4) | \
             _SHIFTL(shiftt, 10, 4) | _SHIFTL(cms, 8, 2) | _SHIFTL(masks, 4, 4) | _SHIFTL(shifts, 0, 4))    \
    }

/*
 *  For RCP 2.0, the maximum number of texels that can be loaded
 *  using a load_block command is 2048.  In order to load the total
 *  4kB of Tmem, change the texel size when loading to be G_IM_SIZ_16b,
 *  then change the tile to the proper texel size after the load.
 *  The g*DPLoadTextureBlock macros already do this, so this change
 *  will be transparent if you use these macros.  If you use
 *  the g*DPLoadBlock macros directly, you will need to handle this
 *  tile manipulation yourself.  RJM.
 */
#define gDPLoadBlock(pkt, tile, uls, ult, lrs, dxt)                                                                    \
    _DW({                                                                                                              \
        Gfx* _g = (Gfx*)(pkt);                                                                                         \
                                                                                                                       \
        _g->words.w0 = (_SHIFTL(G_LOADBLOCK, 24, 8) | _SHIFTL(uls, 12, 12) | _SHIFTL(ult, 0, 12));                     \
        _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL((MIN(lrs, G_TX_LDBLK_MAX_TXL)), 12, 12) | _SHIFTL(dxt, 0, 12)); \
    })

#define gsDPLoadBlock(tile, uls, ult, lrs, dxt)                                                            \
    {                                                                                                      \
        (_SHIFTL(G_LOADBLOCK, 24, 8) | _SHIFTL(uls, 12, 12) | _SHIFTL(ult, 0, 12)),                        \
            (_SHIFTL(tile, 24, 3) | _SHIFTL((MIN(lrs, G_TX_LDBLK_MAX_TXL)), 12, 12) | _SHIFTL(dxt, 0, 12)) \
    }

#define gDPLoadTLUTCmd(pkt, tile, count)                                  \
    _DW({                                                                 \
        Gfx* _g = (Gfx*)pkt;                                              \
                                                                          \
        _g->words.w0 = _SHIFTL(G_LOADTLUT, 24, 8);                        \
        _g->words.w1 = _SHIFTL((tile), 24, 3) | _SHIFTL((count), 14, 10); \
    })

#define gsDPLoadTLUTCmd(tile, count) \
    { _SHIFTL(G_LOADTLUT, 24, 8), _SHIFTL((tile), 24, 3) | _SHIFTL((count), 14, 10) }

#define gDPLoadTextureBlock(pkt, timg, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)          \
    _DW({                                                                                                             \
        gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg);                                                      \
        gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts);       \
        gDPLoadSync(pkt);                                                                                             \
        gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1,                \
                     CALC_DXT(width, siz##_BYTES));                                                                   \
        gDPPipeSync(pkt);                                                                                             \
        gDPSetTile(pkt, fmt, siz, (((width)*siz##_LINE_BYTES) + 7) >> 3, 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \
                   cms, masks, shifts);                                                                               \
        gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC,                               \
                       ((height)-1) << G_TEXTURE_IMAGE_FRAC);                                                         \
    })

#define gDPLoadTextureBlockYuv(pkt, timg, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)    \
    _DW({                                                                                                          \
        gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg);                                                   \
        gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts);    \
        gDPLoadSync(pkt);                                                                                          \
        gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1,             \
                     CALC_DXT(width, siz##_BYTES));                                                                \
        gDPPipeSync(pkt);                                                                                          \
        gDPSetTile(pkt, fmt, siz, (((width)*1) + 7) >> 3, 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \
                   shifts);                                                                                        \
        gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC,                            \
                       ((height)-1) << G_TEXTURE_IMAGE_FRAC);                                                      \
    })

/* Load fix rww 27jun95 */
/* The S at the end means odd lines are already word Swapped */

#define gDPLoadTextureBlockS(pkt, timg, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)         \
    _DW({                                                                                                             \
        gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg);                                                      \
        gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts);       \
        gDPLoadSync(pkt);                                                                                             \
        gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, 0);            \
        gDPPipeSync(pkt);                                                                                             \
        gDPSetTile(pkt, fmt, siz, (((width)*siz##_LINE_BYTES) + 7) >> 3, 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \
                   cms, masks, shifts);                                                                               \
        gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC,                               \
                       ((height)-1) << G_TEXTURE_IMAGE_FRAC);                                                         \
    })

/*
 *  Allow tmem address and render tile to be specified.
 *  The S at the end means odd lines are already word Swapped
 */
#define gDPLoadMultiBlockS(pkt, timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts,     \
                           shiftt)                                                                                   \
    _DW({                                                                                                            \
        gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg);                                                     \
        gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts);   \
        gDPLoadSync(pkt);                                                                                            \
        gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, 0);           \
        gDPPipeSync(pkt);                                                                                            \
        gDPSetTile(pkt, fmt, siz, (((width)*siz##_LINE_BYTES) + 7) >> 3, tmem, rtile, pal, cmt, maskt, shiftt, cms,  \
                   masks, shifts);                                                                                   \
        gDPSetTileSize(pkt, rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC); \
    })

#define gDPLoadTextureBlockYuvS(pkt, timg, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)   \
    _DW({                                                                                                          \
        gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg);                                                   \
        gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts);    \
        gDPLoadSync(pkt);                                                                                          \
        gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, 0);         \
        gDPPipeSync(pkt);                                                                                          \
        gDPSetTile(pkt, fmt, siz, (((width)*1) + 7) >> 3, 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \
                   shifts);                                                                                        \
        gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC,                            \
                       ((height)-1) << G_TEXTURE_IMAGE_FRAC);                                                      \
    })

/*
 *  allows tmem address to be specified
 */
#define _gDPLoadTextureBlock(pkt, timg, tmem, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \
    _DW({                                                                                                           \
        gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg);                                                    \
        gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts);  \
        gDPLoadSync(pkt);                                                                                           \
        gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1,              \
                     CALC_DXT(width, siz##_BYTES));                                                                 \
        gDPPipeSync(pkt);                                                                                           \
        gDPSetTile(pkt, fmt, siz, (((width)*siz##_LINE_BYTES) + 7) >> 3, tmem, G_TX_RENDERTILE, pal, cmt, maskt,    \
                   shiftt, cms, masks, shifts);                                                                     \
        gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC,                             \
                       ((height)-1) << G_TEXTURE_IMAGE_FRAC);                                                       \
    })

/*
 *  allows tmem address and render tile to be specified
 */
#define _gDPLoadTextureBlockTile(pkt, timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, \
                                 shiftt)                                                                               \
    _DW({                                                                                                              \
        gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg);                                                       \
        gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts);     \
        gDPLoadSync(pkt);                                                                                              \
        gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1,                 \
                     CALC_DXT(width, siz##_BYTES));                                                                    \
        gDPPipeSync(pkt);                                                                                              \
        gDPSetTile(pkt, fmt, siz, (((width)*siz##_LINE_BYTES) + 7) >> 3, tmem, rtile, pal, cmt, maskt, shiftt, cms,    \
                   masks, shifts);                                                                                     \
        gDPSetTileSize(pkt, rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC);   \
    })

/*
 *  allows tmem address and render tile to be specified
 */
#define gDPLoadMultiBlock(pkt, timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts,      \
                          shiftt)                                                                                    \
    _DW({                                                                                                            \
        gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg);                                                     \
        gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts);   \
        gDPLoadSync(pkt);                                                                                            \
        gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1,               \
                     CALC_DXT(width, siz##_BYTES));                                                                  \
        gDPPipeSync(pkt);                                                                                            \
        gDPSetTile(pkt, fmt, siz, (((width)*siz##_LINE_BYTES) + 7) >> 3, tmem, rtile, pal, cmt, maskt, shiftt, cms,  \
                   masks, shifts);                                                                                   \
        gDPSetTileSize(pkt, rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC); \
    })

#define gsDPLoadTextureBlock(timg, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)            \
                                                                                                                    \
    gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg),                                                            \
        gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts),         \
        gsDPLoadSync(),                                                                                             \
        gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1,                  \
                      CALC_DXT(width, siz##_BYTES)),                                                                \
        gsDPPipeSync(),                                                                                             \
        gsDPSetTile(fmt, siz, ((((width)*siz##_LINE_BYTES) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \
                    cms, masks, shifts),                                                                            \
        gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC,                                 \
                        ((height)-1) << G_TEXTURE_IMAGE_FRAC)

/* Here is the static form of the pre-swapped texture block loading */
/* See gDPLoadTextureBlockS() for reference.  Basically, just don't
   calculate DxT, use 0 */

#define gsDPLoadTextureBlockS(timg, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)              \
                                                                                                                       \
    gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg),                                                               \
        gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts),            \
        gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, 0), \
        gsDPPipeSync(),                                                                                                \
        gsDPSetTile(fmt, siz, ((((width)*siz##_LINE_BYTES) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt,    \
                    cms, masks, shifts),                                                                               \
        gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC,                                    \
                        ((height)-1) << G_TEXTURE_IMAGE_FRAC)

/*
 *  Allow tmem address to be specified
 */
#define _gsDPLoadTextureBlock(timg, tmem, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)        \
                                                                                                                       \
    gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg),                                                               \
        gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts),         \
        gsDPLoadSync(),                                                                                                \
        gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1,                     \
                      CALC_DXT(width, siz##_BYTES)),                                                                   \
        gsDPPipeSync(),                                                                                                \
        gsDPSetTile(fmt, siz, ((((width)*siz##_LINE_BYTES) + 7) >> 3), tmem, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \
                    cms, masks, shifts),                                                                               \
        gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC,                                    \
                        ((height)-1) << G_TEXTURE_IMAGE_FRAC)

/*
 *  Allow tmem address and render_tile to be specified
 */
#define _gsDPLoadTextureBlockTile(timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, \
                                  shiftt)                                                                          \
                                                                                                                   \
    gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg),                                                           \
        gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts),     \
        gsDPLoadSync(),                                                                                            \
        gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1,                 \
                      CALC_DXT(width, siz##_BYTES)),                                                               \
        gsDPPipeSync(),                                                                                            \
        gsDPSetTile(fmt, siz, ((((width)*siz##_LINE_BYTES) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, cms,  \
                    masks, shifts),                                                                                \
        gsDPSetTileSize(rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC)

/*
 *  Allow tmem address and render_tile to be specified, useful when loading
 *  mutilple tiles at a time.
 */
#define gsDPLoadMultiBlock(timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \
                                                                                                                    \
    gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg),                                                            \
        gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts),      \
        gsDPLoadSync(),                                                                                             \
        gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1,                  \
                      CALC_DXT(width, siz##_BYTES)),                                                                \
        gsDPPipeSync(),                                                                                             \
        gsDPSetTile(fmt, siz, ((((width)*siz##_LINE_BYTES) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, cms,   \
                    masks, shifts),                                                                                 \
        gsDPSetTileSize(rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC)

/*
 *  Allows tmem and render tile to be specified.  Useful when loading
 *  several tiles at a time.
 *
 *  Here is the static form of the pre-swapped texture block loading
 *  See gDPLoadTextureBlockS() for reference.  Basically, just don't
 *  calculate DxT, use 0
 */

#define gsDPLoadMultiBlockS(timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)   \
                                                                                                                       \
    gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg),                                                               \
        gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts),         \
        gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, 0), \
        gsDPPipeSync(),                                                                                                \
        gsDPSetTile(fmt, siz, ((((width)*siz##_LINE_BYTES) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, cms,      \
                    masks, shifts),                                                                                    \
        gsDPSetTileSize(rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC)

#define gDPLoadTextureBlock_4b(pkt, timg, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)          \
    _DW({                                                                                                           \
        gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg);                                                        \
        gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts);         \
        gDPLoadSync(pkt);                                                                                           \
        gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, CALC_DXT_4b(width));            \
        gDPPipeSync(pkt);                                                                                           \
        gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \
                   cms, masks, shifts);                                                                             \
        gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC,                             \
                       ((height)-1) << G_TEXTURE_IMAGE_FRAC);                                                       \
    })

/* Load fix rww 27jun95 */
/* The S at the end means odd lines are already word Swapped */

#define gDPLoadTextureBlock_4bS(pkt, timg, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)         \
    _DW({                                                                                                           \
        gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg);                                                        \
        gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts);         \
        gDPLoadSync(pkt);                                                                                           \
        gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, 0);                             \
        gDPPipeSync(pkt);                                                                                           \
        gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \
                   cms, masks, shifts);                                                                             \
        gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC,                             \
                       ((height)-1) << G_TEXTURE_IMAGE_FRAC);                                                       \
    })

/*
 *  4-bit load block.  Useful when loading multiple tiles
 */
#define gDPLoadMultiBlock_4b(pkt, timg, tmem, rtile, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \
    _DW({                                                                                                             \
        gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg);                                                          \
        gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts);        \
        gDPLoadSync(pkt);                                                                                             \
        gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, CALC_DXT_4b(width));              \
        gDPPipeSync(pkt);                                                                                             \
        gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, cms,     \
                   masks, shifts);                                                                                    \
        gDPSetTileSize(pkt, rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC);  \
    })

/*
 *  4-bit load block.  Allows tmem and render tile to be specified.  Useful when
 *  loading multiple tiles.  The S means odd lines are already word swapped.
 */
#define gDPLoadMultiBlock_4bS(pkt, timg, tmem, rtile, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \
    _DW({                                                                                                              \
        gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg);                                                           \
        gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts);         \
        gDPLoadSync(pkt);                                                                                              \
        gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, 0);                                \
        gDPPipeSync(pkt);                                                                                              \
        gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, cms,      \
                   masks, shifts);                                                                                     \
        gDPSetTileSize(pkt, rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC);   \
    })

#define _gDPLoadTextureBlock_4b(pkt, timg, tmem, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)      \
    _DW({                                                                                                              \
        gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg);                                                           \
        gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts);         \
        gDPLoadSync(pkt);                                                                                              \
        gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, CALC_DXT_4b(width));               \
        gDPPipeSync(pkt);                                                                                              \
        gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), tmem, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \
                   cms, masks, shifts);                                                                                \
        gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC,                                \
                       ((height)-1) << G_TEXTURE_IMAGE_FRAC);                                                          \
    })

#define gsDPLoadTextureBlock_4b(timg, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)               \
                                                                                                                     \
    gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg),                                                                 \
        gsDPSetTile(fmt, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts),              \
        gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, CALC_DXT_4b(width)), \
        gsDPPipeSync(),                                                                                              \
        gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, \
                    masks, shifts),                                                                                  \
        gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC,                                  \
                        ((height)-1) << G_TEXTURE_IMAGE_FRAC)

#define gsDPLoadTextureBlock_4bS(timg, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)              \
                                                                                                                     \
    gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg),                                                                 \
        gsDPSetTile(fmt, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts),              \
        gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, 0), gsDPPipeSync(),  \
        gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, \
                    masks, shifts),                                                                                  \
        gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC,                                  \
                        ((height)-1) << G_TEXTURE_IMAGE_FRAC)

/*
 *  4-bit load block.  Allows tmem address and render tile to be specified.
 *  Useful when loading multiple tiles.
 */
#define gsDPLoadMultiBlock_4b(timg, tmem, rtile, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)    \
                                                                                                                     \
    gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg),                                                                 \
        gsDPSetTile(fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts),           \
        gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, CALC_DXT_4b(width)), \
        gsDPPipeSync(),                                                                                              \
        gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, cms, masks, \
                    shifts),                                                                                         \
        gsDPSetTileSize(rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC)

/*
 *  4-bit load block.  Allows tmem address and render tile to be specified.
 *  Useful when loading multiple tiles.  S means odd lines are already swapped.
 */
#define gsDPLoadMultiBlock_4bS(timg, tmem, rtile, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)   \
                                                                                                                     \
    gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg),                                                                 \
        gsDPSetTile(fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts),           \
        gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, 0), gsDPPipeSync(),  \
        gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, cms, masks, \
                    shifts),                                                                                         \
        gsDPSetTileSize(rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC)

/*
 *  Allows tmem address to be specified
 */
#define _gsDPLoadTextureBlock_4b(timg, tmem, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt)        \
                                                                                                                     \
    gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg),                                                                 \
        gsDPSetTile(fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts),           \
        gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, CALC_DXT_4b(width)), \
        gsDPPipeSync(),                                                                                              \
        gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), tmem, G_TX_RENDERTILE, pal, cmt, maskt, shiftt,   \
                    cms, masks, shifts),                                                                             \
        gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC,                                  \
                        ((height)-1) << G_TEXTURE_IMAGE_FRAC)

#ifndef _HW_VERSION_1

#define gDPLoadTextureTile(pkt, timg, fmt, siz, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt,        \
                           shifts, shiftt)                                                                             \
    _DW({                                                                                                              \
        gDPSetTextureImage(pkt, fmt, siz, width, timg);                                                                \
        gDPSetTile(pkt, fmt, siz, (((((lrs) - (uls) + 1) * siz##_TILE_BYTES) + 7) >> 3), 0, G_TX_LOADTILE, 0, cmt,     \
                   maskt, shiftt, cms, masks, shifts);                                                                 \
        gDPLoadSync(pkt);                                                                                              \
        gDPLoadTile(pkt, G_TX_LOADTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC,                  \
                    (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC);                                     \
        gDPPipeSync(pkt);                                                                                              \
        gDPSetTile(pkt, fmt, siz, (((((lrs) - (uls) + 1) * siz##_LINE_BYTES) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, \
                   maskt, shiftt, cms, masks, shifts);                                                                 \
        gDPSetTileSize(pkt, G_TX_RENDERTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC,             \
                       (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC);                                  \
    })

#else /******** WORKAROUND hw 1 load tile bug ********/

#define gDPLoadTextureTile(pkt, timg, fmt, siz, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt,      \
                           shifts, shiftt)                                                                           \
                                                                                                                     \
    {                                                                                                                \
        int _loadtile_i, _loadtile_nw;                                                                               \
        Gfx* _loadtile_temp = pkt;                                                                                   \
        guDPLoadTextureTile(_loadtile_temp, timg, fmt, siz, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, \
                            maskt, shifts, shiftt);                                                                  \
        _loadtile_nw = guGetDPLoadTextureTileSz(ult, lrt) - 1;                                                       \
        for (_loadtile_i = 0; _loadtile_i < _loadtile_nw; _loadtile_i++)                                             \
            pkt;                                                                                                     \
    }

#endif /* HW_VERSION_1 */

/*
 *  Load texture tile.  Allows tmem address and render tile to be specified.
 *  Useful for loading multiple tiles.
 */
#define gDPLoadMultiTile(pkt, timg, tmem, rtile, fmt, siz, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks,    \
                         maskt, shifts, shiftt)                                                                        \
    _DW({                                                                                                              \
        gDPSetTextureImage(pkt, fmt, siz, width, timg);                                                                \
        gDPSetTile(pkt, fmt, siz, (((((lrs) - (uls) + 1) * siz##_TILE_BYTES) + 7) >> 3), tmem, G_TX_LOADTILE, 0, cmt,  \
                   maskt, shiftt, cms, masks, shifts);                                                                 \
        gDPLoadSync(pkt);                                                                                              \
        gDPLoadTile(pkt, G_TX_LOADTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC,                  \
                    (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC);                                     \
        gDPPipeSync(pkt);                                                                                              \
        gDPSetTile(pkt, fmt, siz, (((((lrs) - (uls) + 1) * siz##_LINE_BYTES) + 7) >> 3), tmem, rtile, pal, cmt, maskt, \
                   shiftt, cms, masks, shifts);                                                                        \
        gDPSetTileSize(pkt, rtile, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC,                       \
                       (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC);                                  \
    })

#define gsDPLoadTextureTile(timg, fmt, siz, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, shifts,   \
                            shiftt)                                                                                   \
                                                                                                                      \
    gsDPSetTextureImage(fmt, siz, width, timg),                                                                       \
        gsDPSetTile(fmt, siz, (((((lrs) - (uls) + 1) * siz##_TILE_BYTES) + 7) >> 3), 0, G_TX_LOADTILE, 0, cmt, maskt, \
                    shiftt, cms, masks, shifts),                                                                      \
        gsDPLoadSync(),                                                                                               \
        gsDPLoadTile(G_TX_LOADTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC,                     \
                     (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC),                                   \
        gsDPPipeSync(),                                                                                               \
        gsDPSetTile(fmt, siz, (((((lrs) - (uls) + 1) * siz##_LINE_BYTES) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt,    \
                    maskt, shiftt, cms, masks, shifts),                                                               \
        gsDPSetTileSize(G_TX_RENDERTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC,                \
                        (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC)

/*
 *  Load texture tile.  Allows tmem address and render tile to be specified.
 *  Useful for loading multiple tiles.
 */
#define gsDPLoadMultiTile(timg, tmem, rtile, fmt, siz, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, \
                          shifts, shiftt)                                                                              \
                                                                                                                       \
    gsDPSetTextureImage(fmt, siz, width, timg),                                                                        \
        gsDPSetTile(fmt, siz, (((((lrs) - (uls) + 1) * siz##_TILE_BYTES) + 7) >> 3), tmem, G_TX_LOADTILE, 0, cmt,      \
                    maskt, shiftt, cms, masks, shifts),                                                                \
        gsDPLoadSync(),                                                                                                \
        gsDPLoadTile(G_TX_LOADTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC,                      \
                     (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC),                                    \
        gsDPPipeSync(),                                                                                                \
        gsDPSetTile(fmt, siz, (((((lrs) - (uls) + 1) * siz##_LINE_BYTES) + 7) >> 3), tmem, rtile, pal, cmt, maskt,     \
                    shiftt, cms, masks, shifts),                                                                       \
        gsDPSetTileSize(rtile, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC,                           \
                        (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC)

#define gDPLoadTextureTile_4b(pkt, timg, fmt, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, shifts, \
                              shiftt)                                                                                 \
    _DW({                                                                                                             \
        gDPSetTextureImage(pkt, fmt, G_IM_SIZ_8b, ((width) >> 1), timg);                                              \
        gDPSetTile(pkt, fmt, G_IM_SIZ_8b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), 0, G_TX_LOADTILE, 0, cmt, maskt,   \
                   shiftt, cms, masks, shifts);                                                                       \
        gDPLoadSync(pkt);                                                                                             \
        gDPLoadTile(pkt, G_TX_LOADTILE, (uls) << (G_TEXTURE_IMAGE_FRAC - 1), (ult) << (G_TEXTURE_IMAGE_FRAC),         \
                    (lrs) << (G_TEXTURE_IMAGE_FRAC - 1), (lrt) << (G_TEXTURE_IMAGE_FRAC));                            \
        gDPPipeSync(pkt);                                                                                             \
        gDPSetTile(pkt, fmt, G_IM_SIZ_4b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt,      \
                   maskt, shiftt, cms, masks, shifts);                                                                \
        gDPSetTileSize(pkt, G_TX_RENDERTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC,            \
                       (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC);                                 \
    })

/*
 *  Load texture tile.  Allows tmem address and render tile to be specified.
 *  Useful for loading multiple tiles.
 */
#define gDPLoadMultiTile_4b(pkt, timg, tmem, rtile, fmt, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks,      \
                            maskt, shifts, shiftt)                                                                     \
    _DW({                                                                                                              \
        gDPSetTextureImage(pkt, fmt, G_IM_SIZ_8b, ((width) >> 1), timg);                                               \
        gDPSetTile(pkt, fmt, G_IM_SIZ_8b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), tmem, G_TX_LOADTILE, 0, cmt, maskt, \
                   shiftt, cms, masks, shifts);                                                                        \
        gDPLoadSync(pkt);                                                                                              \
        gDPLoadTile(pkt, G_TX_LOADTILE, (uls) << (G_TEXTURE_IMAGE_FRAC - 1), (ult) << (G_TEXTURE_IMAGE_FRAC),          \
                    (lrs) << (G_TEXTURE_IMAGE_FRAC - 1), (lrt) << (G_TEXTURE_IMAGE_FRAC));                             \
        gDPPipeSync(pkt);                                                                                              \
        gDPSetTile(pkt, fmt, G_IM_SIZ_4b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), tmem, rtile, pal, cmt, maskt,       \
                   shiftt, cms, masks, shifts);                                                                        \
        gDPSetTileSize(pkt, rtile, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC,                       \
                       (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC);                                  \
    })

#define gsDPLoadTextureTile_4b(timg, fmt, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, shifts,   \
                               shiftt)                                                                              \
                                                                                                                    \
    gsDPSetTextureImage(fmt, G_IM_SIZ_8b, ((width) >> 1), timg),                                                    \
        gsDPSetTile(fmt, G_IM_SIZ_8b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), 0, G_TX_LOADTILE, 0, cmt, maskt,     \
                    shiftt, cms, masks, shifts),                                                                    \
        gsDPLoadSync(),                                                                                             \
        gsDPLoadTile(G_TX_LOADTILE, (uls) << (G_TEXTURE_IMAGE_FRAC - 1), (ult) << (G_TEXTURE_IMAGE_FRAC),           \
                     (lrs) << (G_TEXTURE_IMAGE_FRAC - 1), (lrt) << (G_TEXTURE_IMAGE_FRAC)),                         \
        gsDPPipeSync(),                                                                                             \
        gsDPSetTile(fmt, G_IM_SIZ_4b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, maskt, \
                    shiftt, cms, masks, shifts),                                                                    \
        gsDPSetTileSize(G_TX_RENDERTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC,              \
                        (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC)

/*
 *  Load texture tile.  Allows tmem address and render tile to be specified.
 *  Useful for loading multiple tiles.
 */
#define gsDPLoadMultiTile_4b(timg, tmem, rtile, fmt, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, \
                             shifts, shiftt)                                                                         \
                                                                                                                     \
    gsDPSetTextureImage(fmt, G_IM_SIZ_8b, ((width) >> 1), timg),                                                     \
        gsDPSetTile(fmt, G_IM_SIZ_8b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), tmem, G_TX_LOADTILE, 0, cmt, maskt,   \
                    shiftt, cms, masks, shifts),                                                                     \
        gsDPLoadSync(),                                                                                              \
        gsDPLoadTile(G_TX_LOADTILE, (uls) << (G_TEXTURE_IMAGE_FRAC - 1), (ult) << (G_TEXTURE_IMAGE_FRAC),            \
                     (lrs) << (G_TEXTURE_IMAGE_FRAC - 1), (lrt) << (G_TEXTURE_IMAGE_FRAC)),                          \
        gsDPPipeSync(),                                                                                              \
        gsDPSetTile(fmt, G_IM_SIZ_4b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, \
                    cms, masks, shifts),                                                                             \
        gsDPSetTileSize(rtile, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC,                         \
                        (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC)

/*
 *  Load a 16-entry palette (for 4-bit CI textures)
 *  Assumes a 16 entry tlut is being loaded, palette # is 0-15
 */
#ifndef _HW_VERSION_1

#define gDPLoadTLUT_pal16(pkt, pal, dram)                                                         \
    _DW({                                                                                         \
        gDPSetTextureImage(pkt, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram);                            \
        gDPTileSync(pkt);                                                                         \
        gDPSetTile(pkt, 0, 0, 0, (256 + (((pal)&0xf) * 16)), G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0); \
        gDPLoadSync(pkt);                                                                         \
        gDPLoadTLUTCmd(pkt, G_TX_LOADTILE, 15);                                                   \
        gDPPipeSync(pkt);                                                                         \
    })

#else /* **** WORKAROUND hardware 1 load_tlut bug ****** */

#define gDPLoadTLUT_pal16(pkt, pal, dram)                                                                             \
                                                                                                                      \
    _gDPLoadTextureBlock(pkt, dram, (256 + (((pal)&0xf) * 16)), G_IM_FMT_RGBA, G_IM_SIZ_16b, 4 * 16, 1, pal, 0, 0, 0, \
                         0, 0, 0)

#endif /* _HW_VERSION_1 */

/*
 *  Load a 16-entry palette (for 4-bit CI textures)
 *  Assumes a 16 entry tlut is being loaded, palette # is 0-15
 */
#ifndef _HW_VERSION_1

#define gsDPLoadTLUT_pal16(pal, dram)                                                                         \
                                                                                                              \
    gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram), gsDPTileSync(),                                \
        gsDPSetTile(0, 0, 0, (256 + (((pal)&0xf) * 16)), G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0), gsDPLoadSync(), \
        gsDPLoadTLUTCmd(G_TX_LOADTILE, 15), gsDPPipeSync()

#else /* **** WORKAROUND hardware 1 load_tlut bug ****** */

#define gsDPLoadTLUT_pal16(pal, dram)                                                                                \
                                                                                                                     \
    _gsDPLoadTextureBlock(dram, (256 + (((pal)&0xf) * 16)), G_IM_FMT_RGBA, G_IM_SIZ_16b, 4 * 16, 1, pal, 0, 0, 0, 0, \
                          0, 0)

#endif /* _HW_VERSION_1 */

/*
 *  Load a 256-entry palette (for 8-bit CI textures)
 *  Assumes a 256 entry tlut is being loaded, palette # is not used
 */
#ifndef _HW_VERSION_1

#define gDPLoadTLUT_pal256(pkt, dram)                                      \
    _DW({                                                                  \
        gDPSetTextureImage(pkt, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram);     \
        gDPTileSync(pkt);                                                  \
        gDPSetTile(pkt, 0, 0, 0, 256, G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0); \
        gDPLoadSync(pkt);                                                  \
        gDPLoadTLUTCmd(pkt, G_TX_LOADTILE, 255);                           \
        gDPPipeSync(pkt);                                                  \
    })

#define gDPLoadTLUT_pal128(pkt, pal, dram)                                                   \
    _DW({                                                                                    \
        gDPSetTextureImage(pkt, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram);                       \
        gDPTileSync(pkt);                                                                    \
        gDPSetTile(pkt, 0, 0, 0, 256 + ((pal)&1) * 128, G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0); \
        gDPLoadSync(pkt);                                                                    \
        gDPLoadTLUTCmd(pkt, G_TX_LOADTILE, 127);                                             \
        gDPPipeSync(pkt);                                                                    \
    })

#else /* **** WORKAROUND hardware 1 load_tlut bug ****** */

#define gDPLoadTLUT_pal256(pkt, dram) \
                                      \
    _gDPLoadTextureBlock(pkt, dram, 256, G_IM_FMT_RGBA, G_IM_SIZ_16b, 4 * 256, 1, 0, 0, 0, 0, 0, 0, 0)

#endif /* _HW_VERSION_1 */

#ifndef _HW_VERSION_1

#define gsDPLoadTLUT_pal256(dram)                                                      \
                                                                                       \
    gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram), gsDPTileSync(),         \
        gsDPSetTile(0, 0, 0, 256, G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0), gsDPLoadSync(), \
        gsDPLoadTLUTCmd(G_TX_LOADTILE, 255), gsDPPipeSync()

#else /* **** WORKAROUND hardware 1 load_tlut bug ****** */

#define gsDPLoadTLUT_pal256(dram) \
                                  \
    _gsDPLoadTextureBlock(dram, 256, G_IM_FMT_RGBA, G_IM_SIZ_16b, 4 * 256, 1, 0, 0, 0, 0, 0, 0, 0)

#endif /* _HW_VERSION_1 */

#ifndef _HW_VERSION_1

#define gDPLoadTLUT(pkt, count, tmemaddr, dram)                                 \
    _DW({                                                                       \
        gDPSetTextureImage(pkt, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram);          \
        gDPTileSync(pkt);                                                       \
        gDPSetTile(pkt, 0, 0, 0, tmemaddr, G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0); \
        gDPLoadSync(pkt);                                                       \
        gDPLoadTLUTCmd(pkt, G_TX_LOADTILE, ((count)-1));                        \
        gDPPipeSync(pkt);                                                       \
    })

#else /* **** WORKAROUND hardware 1 load_tlut bug ****** */

#define gDPLoadTLUT(pkt, count, tmemaddr, dram) \
                                                \
    _gDPLoadTextureBlock(pkt, dram, tmemaddr, G_IM_FMT_RGBA, G_IM_SIZ_16b, 4, count, 0, 0, 0, 0, 0, 0, 0)

#endif /* _HW_VERSION_1 */

#ifndef _HW_VERSION_1

#define gsDPLoadTLUT(count, tmemaddr, dram)                                                 \
                                                                                            \
    gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram), gsDPTileSync(),              \
        gsDPSetTile(0, 0, 0, tmemaddr, G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0), gsDPLoadSync(), \
        gsDPLoadTLUTCmd(G_TX_LOADTILE, ((count)-1)), gsDPPipeSync()

#else /* **** WORKAROUND hardware 1 load_tlut bug ****** */
#define gsDPLoadTLUT(count, tmemaddr, dram) \
                                            \
    _gsDPLoadTextureBlock(dram, tmemaddr, G_IM_FMT_RGBA, G_IM_SIZ_16b, 4, count, 0, 0, 0, 0, 0, 0, 0)

#endif /* _HW_VERSION_1 */

#define gDPSetScissor(pkt, mode, ulx, uly, lrx, lry)                                              \
    _DW({                                                                                         \
        Gfx* _g = (Gfx*)pkt;                                                                      \
                                                                                                  \
        _g->words.w0 = _SHIFTL(G_SETSCISSOR, 24, 8) | _SHIFTL((int)((float)(ulx)*4.0F), 12, 12) | \
                       _SHIFTL((int)((float)(uly)*4.0F), 0, 12);                                  \
        _g->words.w1 = _SHIFTL(mode, 24, 2) | _SHIFTL((int)((float)(lrx)*4.0F), 12, 12) |         \
                       _SHIFTL((int)((float)(lry)*4.0F), 0, 12);                                  \
    })

#define gDPSetScissorFrac(pkt, mode, ulx, uly, lrx, lry)                                                            \
    _DW({                                                                                                           \
        Gfx* _g = (Gfx*)pkt;                                                                                        \
                                                                                                                    \
        _g->words.w0 = _SHIFTL(G_SETSCISSOR, 24, 8) | _SHIFTL((int)((ulx)), 12, 12) | _SHIFTL((int)((uly)), 0, 12); \
        _g->words.w1 = _SHIFTL(mode, 24, 2) | _SHIFTL((int)((lrx)), 12, 12) | _SHIFTL((int)((lry)), 0, 12);         \
    })

#define gsDPSetScissor(mode, ulx, uly, lrx, lry)                                   \
    {                                                                              \
        _SHIFTL(G_SETSCISSOR, 24, 8) | _SHIFTL((int)((float)(ulx)*4.0F), 12, 12) | \
            _SHIFTL((int)((float)(uly)*4.0F), 0, 12),                              \
            _SHIFTL(mode, 24, 2) | _SHIFTL((int)((float)(lrx)*4.0F), 12, 12) |     \
                _SHIFTL((int)((float)(lry)*4.0F), 0, 12)                           \
    }

#define gsDPSetScissorFrac(mode, ulx, uly, lrx, lry)                                                 \
    {                                                                                                \
        _SHIFTL(G_SETSCISSOR, 24, 8) | _SHIFTL((int)((ulx)), 12, 12) | _SHIFTL((int)((uly)), 0, 12), \
            _SHIFTL(mode, 24, 2) | _SHIFTL((int)(lrx), 12, 12) | _SHIFTL((int)(lry), 0, 12)          \
    }

#define gDPFillWideRectangle(pkt, ulx, uly, lrx, lry)                           \
    {                                                                           \
        Gfx *_g0 = (Gfx*)(pkt), *_g1 = (Gfx*)(pkt);                             \
        _g0->words.w0 = _SHIFTL(G_FILLWIDERECT, 24, 8) | _SHIFTL((lrx), 2, 22); \
        _g0->words.w1 = _SHIFTL((lry), 2, 22);                                  \
        _g1->words.w0 = _SHIFTL((ulx), 2, 22);                                  \
        _g1->words.w1 = _SHIFTL((uly), 2, 22);                                  \
    }

/* Fraction never used in fill */
#define gDPFillRectangle(pkt, ulx, uly, lrx, lry)                                                     \
    _DW({                                                                                             \
        Gfx* _g = (Gfx*)(pkt);                                                                        \
                                                                                                      \
        _g->words.w0 = (_SHIFTL(G_FILLRECT, 24, 8) | _SHIFTL((lrx), 14, 10) | _SHIFTL((lry), 2, 10)); \
        _g->words.w1 = (_SHIFTL((ulx), 14, 10) | _SHIFTL((uly), 2, 10));                              \
    })

#define gsDPFillRectangle(ulx, uly, lrx, lry)                                          \
    {                                                                                  \
        (_SHIFTL(G_FILLRECT, 24, 8) | _SHIFTL((lrx), 14, 10) | _SHIFTL((lry), 2, 10)), \
            (_SHIFTL((ulx), 14, 10) | _SHIFTL((uly), 2, 10))                           \
    }

/* like gDPFillRectangle but accepts negative arguments */
#define gDPScisFillRectangle(pkt, ulx, uly, lrx, lry)                                                                 \
    _DW({                                                                                                             \
        Gfx* _g = (Gfx*)(pkt);                                                                                        \
                                                                                                                      \
        _g->words.w0 = (_SHIFTL(G_FILLRECT, 24, 8) | _SHIFTL(MAX((lrx), 0), 14, 10) | _SHIFTL(MAX((lry), 0), 2, 10)); \
        _g->words.w1 = (_SHIFTL(MAX((ulx), 0), 14, 10) | _SHIFTL(MAX((uly), 0), 2, 10));                              \
    })

#define gDPSetConvert(pkt, k0, k1, k2, k3, k4, k5)                                                                  \
    _DW({                                                                                                           \
        Gfx* _g = (Gfx*)(pkt);                                                                                      \
                                                                                                                    \
        _g->words.w0 = (_SHIFTL(G_SETCONVERT, 24, 8) | _SHIFTL(k0, 13, 9) | _SHIFTL(k1, 4, 9) | _SHIFTR(k2, 5, 4)); \
        _g->words.w1 = (_SHIFTL(k2, 27, 5) | _SHIFTL(k3, 18, 9) | _SHIFTL(k4, 9, 9) | _SHIFTL(k5, 0, 9));           \
    })

#define gsDPSetConvert(k0, k1, k2, k3, k4, k5)                                                       \
    {                                                                                                \
        (_SHIFTL(G_SETCONVERT, 24, 8) | _SHIFTL(k0, 13, 9) | _SHIFTL(k1, 4, 9) | _SHIFTL(k2, 5, 4)), \
            (_SHIFTL(k2, 27, 5) | _SHIFTL(k3, 18, 9) | _SHIFTL(k4, 9, 9) | _SHIFTL(k5, 0, 9))        \
    }

#define gDPSetKeyR(pkt, cR, sR, wR)                                                   \
    _DW({                                                                             \
        Gfx* _g = (Gfx*)(pkt);                                                        \
                                                                                      \
        _g->words.w0 = _SHIFTL(G_SETKEYR, 24, 8);                                     \
        _g->words.w1 = (_SHIFTL(wR, 16, 12) | _SHIFTL(cR, 8, 8) | _SHIFTL(sR, 0, 8)); \
    })

#define gsDPSetKeyR(cR, sR, wR) \
    { _SHIFTL(G_SETKEYR, 24, 8), _SHIFTL(wR, 16, 12) | _SHIFTL(cR, 8, 8) | _SHIFTL(sR, 0, 8) }

#define gDPSetKeyGB(pkt, cG, sG, wG, cB, sB, wB)                                                          \
    _DW({                                                                                                 \
        Gfx* _g = (Gfx*)(pkt);                                                                            \
                                                                                                          \
        _g->words.w0 = (_SHIFTL(G_SETKEYGB, 24, 8) | _SHIFTL(wG, 12, 12) | _SHIFTL(wB, 0, 12));           \
        _g->words.w1 = (_SHIFTL(cG, 24, 8) | _SHIFTL(sG, 16, 8) | _SHIFTL(cB, 8, 8) | _SHIFTL(sB, 0, 8)); \
    })

#define gsDPSetKeyGB(cG, sG, wG, cB, sB, wB)                                                  \
    {                                                                                         \
        (_SHIFTL(G_SETKEYGB, 24, 8) | _SHIFTL(wG, 12, 12) | _SHIFTL(wB, 0, 12)),              \
            (_SHIFTL(cG, 24, 8) | _SHIFTL(sG, 16, 8) | _SHIFTL(cB, 8, 8) | _SHIFTL(sB, 0, 8)) \
    }

#define gDPNoParam(pkt, cmd)                \
    _DW({                                   \
        Gfx* _g = (Gfx*)(pkt);              \
                                            \
        _g->words.w0 = _SHIFTL(cmd, 24, 8); \
        _g->words.w1 = 0;                   \
    })

#define gsDPNoParam(cmd) \
    { _SHIFTL(cmd, 24, 8), 0 }

#define gDPParam(pkt, cmd, param)           \
    _DW({                                   \
        Gfx* _g = (Gfx*)(pkt);              \
                                            \
        _g->words.w0 = _SHIFTL(cmd, 24, 8); \
        _g->words.w1 = (param);             \
    })

#define gsDPParam(cmd, param) \
    { _SHIFTL(cmd, 24, 8), (param) }

/* Notice that textured rectangles are 128-bit commands, therefore
 * gsDPTextureRectangle() should not be used in display lists
 * under normal circumstances (use gsSPTextureRectangle()).
 * That is also why there is no gDPTextureRectangle() macros.
 */
#define gsDPTextureRectangle(xl, yl, xh, yh, tile, s, t, dsdx, dtdy)                         \
    {                                                                                        \
        (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)),              \
        (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)),                   \
    },                                                                                       \
    {                                                                                        \
        _SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16), _SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16) \
    }

#define gDPTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy)                       \
    _DW({                                                                                      \
        Gfx* _g = (Gfx*)(pkt);                                                                 \
        if (pkt)                                                                               \
            ;                                                                                  \
        _g->words.w0 = (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)); \
        _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12));      \
        _g++;                                                                                  \
        _g->words.w0 = (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16));                               \
        _g->words.w1 = (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16));                         \
    })

#define gsDPTextureRectangleFlip(xl, yl, xh, yh, tile, s, t, dsdx, dtdy)                     \
    {                                                                                        \
        (_SHIFTL(G_TEXRECTFLIP, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)),          \
        (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)),                   \
    },                                                                                       \
    {                                                                                        \
        _SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16), _SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16) \
    }

#define gDPTextureRectangleFlip(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy)                       \
    _DW({                                                                                          \
        Gfx* _g = (Gfx*)(pkt);                                                                     \
        if (pkt)                                                                                   \
            ;                                                                                      \
        _g->words.w0 = (_SHIFTL(G_TEXRECTFLIP, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)); \
        _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12));          \
        _g++;                                                                                      \
        _g->words.w0 = (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16));                                   \
        _g->words.w1 = (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16));                             \
    })

#define gsSPTextureRectangle(xl, yl, xh, yh, tile, s, t, dsdx, dtdy)        \
    (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)), \
        (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)),  \
        gsImmp1(G_RDPHALF_1, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16))),     \
        gsImmp1(G_RDPHALF_2, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)))

#define gSPTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy)                       \
    _DW({                                                                                      \
        Gfx* _g = (Gfx*)(pkt);                                                                 \
                                                                                               \
        _g->words.w0 = (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)); \
        _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12));      \
        gImmp1(pkt, G_RDPHALF_1, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16)));                    \
        gImmp1(pkt, G_RDPHALF_2, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)));              \
    })

#define gSPWideTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy)   \
    {                                                                          \
        Gfx *_g0 = (Gfx*)(pkt), *_g1 = (Gfx*)(pkt), *_g2 = (Gfx*)(pkt);        \
                                                                               \
        _g0->words.w0 = _SHIFTL(G_TEXRECT_WIDE, 24, 8) | _SHIFTL((xh), 0, 24); \
        _g0->words.w1 = _SHIFTL((yh), 0, 24);                                  \
        _g1->words.w0 = (_SHIFTL(tile, 24, 3) | _SHIFTL((xl), 0, 24));         \
        _g1->words.w1 = _SHIFTL((yl), 0, 24);                                  \
        _g2->words.w0 = (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16));              \
        _g2->words.w1 = (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16));        \
    }

#define gsSPWideTextureRectangle(xl, yl, xh, yh, tile, s, t, dsdx, dtdy)                         \
    { {                                                                                          \
        (_SHIFTL(G_TEXRECT_WIDE, 24, 8) | _SHIFTL((xh), 0, 24)),                                 \
        _SHIFTL((yh), 0, 24),                                                                    \
    } },                                                                                         \
        { {                                                                                      \
            (_SHIFTL((tile), 24, 3) | _SHIFTL((xl), 0, 24)),                                     \
            _SHIFTL((yl), 0, 24),                                                                \
        } },                                                                                     \
    {                                                                                            \
        { _SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16), _SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16) } \
    }

/* like gSPTextureRectangle but accepts negative position arguments */
#define gSPScisTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy)                                       \
    _DW({                                                                                                          \
        Gfx* _g = (Gfx*)(pkt);                                                                                     \
                                                                                                                   \
        _g->words.w0 =                                                                                             \
            (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(MAX((s16)(xh), 0), 12, 12) | _SHIFTL(MAX((s16)(yh), 0), 0, 12));  \
        _g->words.w1 =                                                                                             \
            (_SHIFTL((tile), 24, 3) | _SHIFTL(MAX((s16)(xl), 0), 12, 12) | _SHIFTL(MAX((s16)(yl), 0), 0, 12));     \
        gImmp1(pkt, G_RDPHALF_1,                                                                                   \
               (_SHIFTL(((s) - (((s16)(xl) < 0) ? (((s16)(dsdx) < 0) ? (MAX((((s16)(xl) * (s16)(dsdx)) >> 7), 0))  \
                                                                     : (MIN((((s16)(xl) * (s16)(dsdx)) >> 7), 0))) \
                                                : 0)),                                                             \
                        16, 16) |                                                                                  \
                _SHIFTL(((t) - (((yl) < 0) ? (((s16)(dtdy) < 0) ? (MAX((((s16)(yl) * (s16)(dtdy)) >> 7), 0))       \
                                                                : (MIN((((s16)(yl) * (s16)(dtdy)) >> 7), 0)))      \
                                           : 0)),                                                                  \
                        0, 16)));                                                                                  \
        gImmp1(pkt, G_RDPHALF_2, (_SHIFTL((dsdx), 16, 16) | _SHIFTL((dtdy), 0, 16)));                              \
    })

#define gsSPTextureRectangleFlip(xl, yl, xh, yh, tile, s, t, dsdx, dtdy)        \
    (_SHIFTL(G_TEXRECTFLIP, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)), \
        (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)),      \
        gsImmp1(G_RDPHALF_1, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16))),         \
        gsImmp1(G_RDPHALF_2, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)))

#define gSPTextureRectangleFlip(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy)                       \
    _DW({                                                                                          \
        Gfx* _g = (Gfx*)(pkt);                                                                     \
                                                                                                   \
        _g->words.w0 = (_SHIFTL(G_TEXRECTFLIP, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)); \
        _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12));          \
        gImmp1(pkt, G_RDPHALF_1, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16)));                        \
        gImmp1(pkt, G_RDPHALF_2, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)));                  \
    })

#define gsDPWord(wordhi, wordlo) \
    gsImmp1(G_RDPHALF_1, (unsigned int)(wordhi)), gsImmp1(G_RDPHALF_2, (unsigned int)(wordlo))

#define gDPWord(pkt, wordhi, wordlo)                      \
    _DW({                                                 \
        Gfx* _g = (Gfx*)(pkt);                            \
                                                          \
        gImmp1(pkt, G_RDPHALF_1, (unsigned int)(wordhi)); \
        gImmp1(pkt, G_RDPHALF_2, (unsigned int)(wordlo)); \
    })

#define gDPFullSync(pkt) gDPNoParam(pkt, G_RDPFULLSYNC)
#define gsDPFullSync() gsDPNoParam(G_RDPFULLSYNC)
#define gDPTileSync(pkt) gDPNoParam(pkt, G_RDPTILESYNC)
#define gsDPTileSync() gsDPNoParam(G_RDPTILESYNC)
#define gDPPipeSync(pkt) gDPNoParam(pkt, G_RDPPIPESYNC)
#define gsDPPipeSync() gsDPNoParam(G_RDPPIPESYNC)
#define gDPLoadSync(pkt) gDPNoParam(pkt, G_RDPLOADSYNC)
#define gsDPLoadSync() gsDPNoParam(G_RDPLOADSYNC)
#define gDPNoOp(pkt) gDPNoParam(pkt, G_NOOP)
#define gsDPNoOp() gsDPNoParam(G_NOOP)
#define gDPNoOpTag(pkt, tag) gDPParam(pkt, G_NOOP, tag)
#define gsDPNoOpTag(tag) gsDPParam(G_NOOP, tag)

#define gDPNoOpHere(pkt, file, line) gDma1p(pkt, G_NOOP, file, line, 1)
#define gDPNoOpString(pkt, data, n) gDma1p(pkt, G_NOOP, data, n, 2)
#define gDPNoOpWord(pkt, data, n) gDma1p(pkt, G_NOOP, data, n, 3)
#define gDPNoOpFloat(pkt, data, n) gDma1p(pkt, G_NOOP, data, n, 4)
#define gDPNoOpQuiet(pkt) gDma1p(pkt, G_NOOP, 0, 0, 5)
#define gDPNoOpVerbose(pkt, n) gDma1p(pkt, G_NOOP, 0, n, 5)
#define gDPNoOpCallBack(pkt, callback, arg) gDma1p(pkt, G_NOOP, callback, arg, 6)
#define gDPNoOpOpenDisp(pkt, file, line) gDma1p(pkt, G_NOOP, file, line, 7)
#define gDPNoOpCloseDisp(pkt, file, line) gDma1p(pkt, G_NOOP, file, line, 8)
#define gDPNoOpTag3(pkt, type, data, n) gDma1p(pkt, G_NOOP, data, n, type)

#endif

#endif
#endif
